home-hosted 0.8.2 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -5
- package/README.md +50 -14
- package/dist/cli.js +1331 -282
- package/dist/cli.js.map +1 -1
- package/docs/DDNS.md +2 -1
- package/docs/NOTIFICATIONS.md +72 -20
- package/docs/SERVERS.md +20 -1
- package/docs/UI_CREATION.md +22 -7
- package/docs/branding/logo.svg +33 -0
- package/docs/branding/social-preview.png +0 -0
- package/package.json +27 -24
- package/uis/stock/dist/assets/de-DL4xkOYj.js +1 -0
- package/uis/stock/dist/assets/es-CKj-v3FY.js +1 -0
- package/uis/stock/dist/assets/fr-72WremqT.js +1 -0
- package/uis/stock/dist/assets/index-BNSSU69y.js +47 -0
- package/uis/stock/dist/assets/index-GxTN2fYn.css +2 -0
- package/uis/stock/dist/assets/ja-BU4XyMbC.js +1 -0
- package/uis/stock/dist/assets/ko-Db8xtJ0N.js +1 -0
- package/uis/stock/dist/assets/ru-Bs8KXP2O.js +1 -0
- package/uis/stock/dist/assets/vi-DdQD4h7B.js +1 -0
- package/uis/stock/dist/assets/zh-CN-DIrw2Ubv.js +1 -0
- package/uis/stock/dist/favicon.ico +0 -0
- package/uis/stock/dist/favicon.svg +14 -0
- package/uis/stock/dist/index.html +6 -2
- package/uis/stock/dist/ui.json +3 -3
- package/uis/stock/dist/assets/de-DQxDtYp2.js +0 -1
- package/uis/stock/dist/assets/es-BP0qzCTU.js +0 -1
- package/uis/stock/dist/assets/fr-DHCvxiys.js +0 -1
- package/uis/stock/dist/assets/index-C02FUAwz.js +0 -46
- package/uis/stock/dist/assets/index-Skf2FzpK.css +0 -2
- package/uis/stock/dist/assets/ja-DseIeS1D.js +0 -1
- package/uis/stock/dist/assets/ko-Dcu2b6c2.js +0 -1
- package/uis/stock/dist/assets/ru-DaAAVw09.js +0 -1
- package/uis/stock/dist/assets/vi-D780h-t9.js +0 -1
- package/uis/stock/dist/assets/zh-CN-C8OC-N1b.js +0 -1
package/docs/DDNS.md
CHANGED
|
@@ -81,7 +81,8 @@ public DNS API).
|
|
|
81
81
|
|
|
82
82
|
Telegram carries one message per pass that changed a record, and one for a pass that failed — the
|
|
83
83
|
same switch as every other event, under Workspace Settings → Notifications → *Dynamic DNS changes and
|
|
84
|
-
failures* (`
|
|
84
|
+
failures* (`notifications.defaults.onDdns`, or a destination's own when it carries its own
|
|
85
|
+
settings). There is no second switch in this section.
|
|
85
86
|
|
|
86
87
|
## Adding a provider
|
|
87
88
|
|
package/docs/NOTIFICATIONS.md
CHANGED
|
@@ -1,16 +1,31 @@
|
|
|
1
1
|
# Notifications
|
|
2
2
|
|
|
3
|
-
home-hosted
|
|
4
|
-
|
|
3
|
+
home-hosted pushes supervision events to any number of **destinations**. A destination is a Telegram
|
|
4
|
+
chat or a webhook — the same list, because both are just somewhere an alert lands, and you may have
|
|
5
|
+
several of either.
|
|
6
|
+
|
|
7
|
+
Two settings groups, and the split is the point:
|
|
8
|
+
|
|
9
|
+
- **General** — which events fire, and the throttle. Set **once**.
|
|
10
|
+
- **Destinations** — where they go. Each one has an on/off switch and an **Own settings** toggle:
|
|
11
|
+
off (the default for a new one) means it follows the general block entirely; on means it carries
|
|
12
|
+
its own toggles and throttle.
|
|
13
|
+
|
|
14
|
+
So "tell me about crashes and host thresholds, throttled every two minutes, to my phone and the
|
|
15
|
+
group chat" is configured once and pointed at two places. A send never blocks supervision: a failure
|
|
16
|
+
is logged and recorded against that destination alone, and the others still go out.
|
|
5
17
|
|
|
6
18
|
## Setup
|
|
7
19
|
|
|
8
20
|
1. Talk to [@BotFather](https://t.me/BotFather), run `/newbot`, and copy the token it prints.
|
|
9
|
-
2. In the panel: **Workspace Settings → Notifications
|
|
10
|
-
3.
|
|
21
|
+
2. In the panel: **Workspace Settings → Notifications → Destinations**, press **Add Telegram**.
|
|
22
|
+
3. Paste the token into that destination's box and press **Save token**, then **Detect chats**.
|
|
23
|
+
4. Send your bot a message from the chat you want the alerts in — a bot cannot open a conversation —
|
|
11
24
|
then pick that chat from the list (or paste its id).
|
|
12
|
-
|
|
13
|
-
|
|
25
|
+
5. Press **Send a test**: the chat gets a message, and the panel shows the result and its timestamp.
|
|
26
|
+
6. Leave **Own settings** off to follow the general block, or turn it on to give this chat its own.
|
|
27
|
+
|
|
28
|
+
A second chat is another **Add Telegram** — it gets its own bot token, stored under its own id.
|
|
14
29
|
|
|
15
30
|
<details>
|
|
16
31
|
<summary><b>Doing it without the UI</b></summary>
|
|
@@ -19,23 +34,23 @@ opt-in, and the bot token never leaves the secrets file.
|
|
|
19
34
|
TOKEN='123456:ABC...'
|
|
20
35
|
WS='?workspace=default' # every notifications route is workspace-scoped; omit for the default one
|
|
21
36
|
|
|
22
|
-
curl -X PUT "http://127.0.0.1:3999/api/notifications/token$WS" \
|
|
37
|
+
curl -X PUT "http://127.0.0.1:3999/api/notifications/destinations/alerts/token$WS" \
|
|
23
38
|
-H 'content-type: application/json' -H "Authorization: Bearer $HH_TOKEN" \
|
|
24
39
|
-d "{\"botToken\":\"$TOKEN\"}"
|
|
25
40
|
|
|
26
41
|
# which chats can this bot see (after you message it once)?
|
|
27
|
-
curl -X POST "http://127.0.0.1:3999/api/notifications/detect-chats$WS" \
|
|
42
|
+
curl -X POST "http://127.0.0.1:3999/api/notifications/destinations/alerts/detect-chats$WS" \
|
|
28
43
|
-H 'content-type: application/json' -H "Authorization: Bearer $HH_TOKEN" \
|
|
29
44
|
-d "{\"botToken\":\"$TOKEN\"}"
|
|
30
45
|
|
|
31
|
-
curl -X POST "http://127.0.0.1:3999/api/notifications/test$WS" \
|
|
46
|
+
curl -X POST "http://127.0.0.1:3999/api/notifications/destinations/alerts/test$WS" \
|
|
32
47
|
-H 'content-type: application/json' -H "Authorization: Bearer $HH_TOKEN" \
|
|
33
48
|
-d '{"chatId":"123456789"}'
|
|
34
49
|
```
|
|
35
50
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
51
|
+
Every Telegram route names a destination id (`alerts` here), and the token is stored under it — which
|
|
52
|
+
is what lets one workspace point at several chats. `detect-chats` accepts a token override, so one
|
|
53
|
+
can be tried before it is saved; `DELETE /api/notifications/destinations/alerts/token` removes it.
|
|
39
54
|
|
|
40
55
|
</details>
|
|
41
56
|
|
|
@@ -55,22 +70,59 @@ Host thresholds themselves — disk, memory, swap, load, temperature — are **G
|
|
|
55
70
|
vitals**. Dynamic DNS carries one message per pass that changed a record, and one per failed pass —
|
|
56
71
|
see [DDNS.md](./DDNS.md).
|
|
57
72
|
|
|
58
|
-
##
|
|
73
|
+
## Webhooks
|
|
74
|
+
|
|
75
|
+
A webhook is a URL, a method, a body format and a set of headers — a **list**, so one panel can alert
|
|
76
|
+
several places. It is what covers everything other than Telegram, because those services differ only
|
|
77
|
+
in the shape of the request:
|
|
78
|
+
|
|
79
|
+
| | URL | Body | Headers |
|
|
80
|
+
| --- | --- | --- | --- |
|
|
81
|
+
| **ntfy** | `https://ntfy.sh/<topic>` | `plain text` | `Title: home-hosted` (ntfy reads the title here), `Priority: default`, and `Authorization: Bearer <token>` when the topic needs one |
|
|
82
|
+
| **Discord** | your webhook URL | `JSON` | none — the payload fills `content` |
|
|
83
|
+
| **Slack** | your webhook URL | `JSON` | none — the payload fills `text` |
|
|
84
|
+
| **anything else** | your URL | either | whatever it needs |
|
|
85
|
+
|
|
86
|
+
The JSON body carries the same event under several names (`text`, `content`, `title`, `message`,
|
|
87
|
+
`reason`, `server`, `label`, `at`), so a receiver reads whichever it knows. `format: text` sends the
|
|
88
|
+
message alone, which is what ntfy's publish API expects — pointed at ntfy with `format: json` it
|
|
89
|
+
would show the JSON itself as the notification.
|
|
59
90
|
|
|
60
|
-
`
|
|
61
|
-
|
|
62
|
-
|
|
91
|
+
Headers are `Name: value` per line, with the split on the **first** colon so a token containing one
|
|
92
|
+
survives. `${ENV}` expands in the URL and in the values, the same rule the rest of the config follows
|
|
93
|
+
— so a token stays out of `settings.json` entirely:
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{ "headers": "Authorization: Bearer ${HH_WEBHOOK_TOKEN}" }
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
A literal token in a header works too, but it then lives in the workspace's `settings.json` — a plain
|
|
100
|
+
config file. Prefer the placeholder above, and treat that file as sensitive if you do use one.
|
|
101
|
+
|
|
102
|
+
Two destinations cannot share an `id`: it keys a destination's throttle, its last-result record **and
|
|
103
|
+
a Telegram destination's bot token**, so a duplicate would make two behave as one. The save is
|
|
104
|
+
refused and names the id, and so is a webhook whose URL is missing or not http(s) — an enabled
|
|
105
|
+
destination that can never send.
|
|
106
|
+
|
|
107
|
+
**Send a test** on a destination tries the values *in the form*, so an unsaved URL and header block
|
|
108
|
+
are what gets exercised.
|
|
63
109
|
|
|
64
110
|
## Where the token lives
|
|
65
111
|
|
|
66
112
|
The workspace's `.hh/<workspace>/.secrets.json`, mode `0600`, next to that workspace's DDNS
|
|
67
|
-
credentials. The password and API-token hashes are panel-wide and live in
|
|
113
|
+
credentials, as a map from **destination id** to bot token. The password and API-token hashes are panel-wide and live in
|
|
68
114
|
`.hh/.control-secrets.json`. The token is never written into `servers.config.json`, so committing or
|
|
69
115
|
sharing a config cannot leak it — and `GET /api/settings/workspace` reports `tokenSet: true|false`,
|
|
70
116
|
never the token itself.
|
|
71
117
|
|
|
72
118
|
## Delivery failures
|
|
73
119
|
|
|
74
|
-
Sends are fire-and-forget: supervision never waits on a chat API. A failure is logged and
|
|
75
|
-
|
|
76
|
-
|
|
120
|
+
Sends are fire-and-forget: supervision never waits on a chat or webhook API. A failure is logged, and
|
|
121
|
+
the last outcome is recorded per destination and shown on its row, so a wrong chat id or an
|
|
122
|
+
unreachable URL is visible there instead of silently swallowing alerts.
|
|
123
|
+
|
|
124
|
+
## Throttling
|
|
125
|
+
|
|
126
|
+
`cooldownMs` (default two minutes) is per **destination, server and reason**, so a flapping process
|
|
127
|
+
cannot flood any one destination — and one destination inside its window never silences another. It
|
|
128
|
+
lives in the general block, or on a destination that has **Own settings** on.
|
package/docs/SERVERS.md
CHANGED
|
@@ -36,8 +36,9 @@ servers and its state together.
|
|
|
36
36
|
| `onPortConflict` | `block` (default), `warn`, `follow`, `reclaim`, or `kill` — see below |
|
|
37
37
|
| `persistent` | run it under its own nanny so it survives the panel — see below |
|
|
38
38
|
| `health.mode` | `port` (TCP connect) or `http` (path, expected status, expected body — a `HEAD` carries no body, so it is warned about and the body check is skipped) |
|
|
39
|
+
| `health.http.scheme`, `health.http.allowInvalidCert` | `http` (default) or `https` for a backend that serves TLS itself; the second accepts a self-signed certificate, which is the usual case on a home network. A certificate is verified unless you say otherwise |
|
|
39
40
|
| `health.unhealthyThreshold`, `forceRestartAfterMs` | how many failed probes before the card warns, and when to restart anyway |
|
|
40
|
-
| `restart.*` | backoff: `maxRetries`, `baseDelayMs`, `factor`, `maxDelayMs`, `resetAfterMs` |
|
|
41
|
+
| `restart.*` | backoff: `maxRetries`, `baseDelayMs`, `factor`, `maxDelayMs`, `resetAfterMs` — the spent budget is remembered on disk, so a crash loop cannot outlast `maxRetries` by outliving the panel |
|
|
41
42
|
| `stop.*` | `signal`, `killGroup`, `graceMs`, and `killPortHolders` to sweep a leftover listener |
|
|
42
43
|
| `dependsOn` | ids that must be healthy first. `stop-all`/`down` stop dependents before their dependencies; stopping **one** server does not cascade — the log names any dependent it left running |
|
|
43
44
|
| `logBufferLines` | how many lines of live output the panel keeps in memory per entry (50–100000, default 500). The file on disk is separate: this is the buffer the Logs page streams from |
|
|
@@ -136,6 +137,24 @@ set onPortConflict to "follow" to adopt it, "reclaim" to replace it with a super
|
|
|
136
137
|
or "kill" to stop whatever holds the port
|
|
137
138
|
```
|
|
138
139
|
|
|
140
|
+
### An entry with no port that outlived the panel
|
|
141
|
+
|
|
142
|
+
A port is what makes the checks above possible, so an entry with **no port** has none of them. A
|
|
143
|
+
non-persistent child is spawned detached, so it survives a panel killed hard — while nothing recorded
|
|
144
|
+
that it existed. With no port to check, the next boot started a **second copy** of a server already
|
|
145
|
+
running: the dangerous kind of duplicate for a worker.
|
|
146
|
+
|
|
147
|
+
Every spawn now also records the child's pid and start time, consulted before spawning:
|
|
148
|
+
|
|
149
|
+
- alive, **born when the record says** (a recycled pid is refused), and carrying `HHOSTED_SERVER_ID`
|
|
150
|
+
→ adopted, exactly like a successor above;
|
|
151
|
+
- gone → the record is dropped and the start proceeds;
|
|
152
|
+
- alive but the platform **cannot confirm whose it is** → refused with the pid named, rather than
|
|
153
|
+
duplicated. Stop that pid, or use `persistent: true`, which is the supported way to own a portless
|
|
154
|
+
process across a panel restart.
|
|
155
|
+
|
|
156
|
+
An entry with a `port` is unaffected: its conflict policy stays the only witness.
|
|
157
|
+
|
|
139
158
|
### The `kill` policy
|
|
140
159
|
|
|
141
160
|
`follow` and `reclaim` need to know *whose* port it is. When they cannot answer that — a program that
|
package/docs/UI_CREATION.md
CHANGED
|
@@ -45,9 +45,7 @@ installed, and what lets it offer you the next version:
|
|
|
45
45
|
"name": "my-panel",
|
|
46
46
|
"version": "2.1.0",
|
|
47
47
|
"repo": "you/my-panel",
|
|
48
|
-
"
|
|
49
|
-
"asset": "my-panel.zip",
|
|
50
|
-
"unix": 1790366625
|
|
48
|
+
"asset": "my-panel.zip"
|
|
51
49
|
}
|
|
52
50
|
```
|
|
53
51
|
|
|
@@ -55,9 +53,12 @@ installed, and what lets it offer you the next version:
|
|
|
55
53
|
| --- | --- |
|
|
56
54
|
| `name`, `version` | what Global settings → Interface shows as installed |
|
|
57
55
|
| `repo` | `owner/name` the releases come from — the one field `ui-update` needs |
|
|
58
|
-
| `tag` | the release this build came from, so an update can tell newer from older |
|
|
59
56
|
| `asset` | the release asset name, when a release carries more than one UI |
|
|
60
|
-
|
|
57
|
+
|
|
58
|
+
**`tag` and `unix` are not yours to write.** They describe the *build*, and the panel's own
|
|
59
|
+
`build-uis.mjs` stamps them into `dist/ui.json` — a committed value would be a release behind by the
|
|
60
|
+
time the zip is cut, which is what made `ui-update` reinstall the same UI on every boot. A UI built
|
|
61
|
+
outside this repo sets neither and loses nothing: `ui-update` then compares against your release list.
|
|
61
62
|
|
|
62
63
|
With those set, `home-hosted ui-update` lists the releases carrying your `asset` and installs the one
|
|
63
64
|
you pick; `--old` lists older ones. A version bump in your own repo is enough — nothing about the
|
|
@@ -146,7 +147,7 @@ naming one that does not exist is a `404 UNKNOWN_WORKSPACE`, never a silent fall
|
|
|
146
147
|
| `POST` / `DELETE /api/notifications/token`, `/api/notifications/test`, `/detect-chats` | Telegram, per workspace |
|
|
147
148
|
| `GET` / `PUT /api/ddns`, `PUT` / `DELETE /api/ddns/credentials/:id`, `POST /api/ddns/check` | dynamic DNS, per workspace: the policy plus live per-record state, one account's sealed credentials (write-only — the view exposes ids and whether each is set, never the values), and a pass run now |
|
|
148
149
|
| `POST /api/auth/login`, `GET /api/auth/session`, `POST /api/auth/logout` | the session |
|
|
149
|
-
| `GET /healthz` | liveness — **no session**, and `503` when an autostart server has crashed |
|
|
150
|
+
| `GET /healthz` | liveness — **no session**, and `503` when an autostart server has crashed or a running one is failing its health check |
|
|
150
151
|
| `GET /api/metrics` | Prometheus text (needs a session) |
|
|
151
152
|
|
|
152
153
|
`GET /healthz` and `GET /openapi/*` are the only unauthenticated reads; the SPA shell itself is
|
|
@@ -170,6 +171,11 @@ person never chose freezes it against those defaults, so build the body as a dif
|
|
|
170
171
|
from `src/shared/patch-diff.ts`, while comparing against the defaults (`inheritBaseline`) is each
|
|
171
172
|
UI's own job — see `uis/stock/src/components/server/addServerForm.ts`.
|
|
172
173
|
|
|
174
|
+
Each server view carries a `history` window. A `start` event may carry an optional **`actor`** —
|
|
175
|
+
`policy`, `user`, `agent` or `boot` — naming what asked for that start: the panel itself (a
|
|
176
|
+
health-forced restart or a backoff retry), a browser, a token request, or the autostart pass. Absent
|
|
177
|
+
on events written before it existed, so read it defensively.
|
|
178
|
+
|
|
173
179
|
### Failures
|
|
174
180
|
|
|
175
181
|
Every failing request answers with one envelope:
|
|
@@ -236,6 +242,15 @@ Two things the API will not tell you, and both are what people report as bugs:
|
|
|
236
242
|
guard reads that late arrival as a pending edit, leaves those fields showing schema defaults
|
|
237
243
|
forever, and offers to "revert" a change nobody made. Fill each block on its own, and only while
|
|
238
244
|
that block is untouched since it was last filled (`uis/stock/src/components/settings/settingsForm.ts`).
|
|
245
|
+
- **A block's snapshot must cover every field the block's form holds — including a list.**
|
|
246
|
+
The guard is "has this block been edited since it was filled?", and it answers by comparing the
|
|
247
|
+
snapshot against the form. Leave one field out of the snapshot and an edit to *it* never counts
|
|
248
|
+
as an edit: the next frame re-fills the block over it, every few seconds, for as long as the
|
|
249
|
+
panel ticks. This has shipped twice — the Notifications block snapshotted only its Telegram
|
|
250
|
+
policy while its form also held the webhook list, so typed webhooks vanished on each frame; and
|
|
251
|
+
the `control` group grew a per-part guard for the same reason. When a block gains a field or a
|
|
252
|
+
sibling list, extend the snapshot in the same commit as the form, and keep the two side by side
|
|
253
|
+
so the omission is visible.
|
|
239
254
|
- **A boolean setting is a switch, not a checkbox.** Keep checkboxes for picking items out of a set
|
|
240
255
|
(a restore plan), where the control is the list entry. `uis/stock/src/components/ui/ToggleSwitch.vue`
|
|
241
256
|
is the reference.
|
|
@@ -298,4 +313,4 @@ with an installed panel on the default 3999; override with `--port`/`--ui-port`
|
|
|
298
313
|
- [ ] deep links render (the server falls back to `index.html`) and each page has its own path
|
|
299
314
|
- [ ] assets self-hosted, no CDN
|
|
300
315
|
- [ ] works over plain http on a LAN
|
|
301
|
-
- [ ] `ui.json` with a name and version, so Global settings can tell you what is installed — plus `repo`/`
|
|
316
|
+
- [ ] `ui.json` with a name and version, so Global settings can tell you what is installed — plus `repo`/`asset` if you want `ui-update` to follow your releases
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" role="img" aria-label="home-hosted">
|
|
2
|
+
<title>home-hosted</title>
|
|
3
|
+
<!--
|
|
4
|
+
The mark: a house whose interior is a rack. Authored as geometry rather than traced from
|
|
5
|
+
a bitmap, so it stays crisp at 16px (favicon) and sharp at any size.
|
|
6
|
+
|
|
7
|
+
Grid: 512 square. Walls centred at x=84 and x=428 with a 28 stroke, so the interior runs
|
|
8
|
+
98-414. The roof is one stroke whose mitre closes the apex. The roof sits high and the
|
|
9
|
+
floor low so the three rack units have even gaps instead of crowding either end; the top
|
|
10
|
+
unit carries the lit slot, which is what the product is actually about.
|
|
11
|
+
-->
|
|
12
|
+
<path d="M84 188 L256 64 L428 188 L428 460 L84 460 Z"
|
|
13
|
+
fill="none" stroke="#2f5fe0" stroke-width="28" stroke-linejoin="miter" stroke-linecap="round"/>
|
|
14
|
+
<g fill="#2f5fe0">
|
|
15
|
+
<rect x="152" y="216" width="208" height="52" rx="13"/>
|
|
16
|
+
<rect x="152" y="290" width="208" height="52" rx="13"/>
|
|
17
|
+
<rect x="152" y="364" width="208" height="52" rx="13"/>
|
|
18
|
+
</g>
|
|
19
|
+
<g fill="#ffffff">
|
|
20
|
+
<!-- Slot vents: three per unit on one rhythm, and the top unit's run stops short of the dot. -->
|
|
21
|
+
<rect x="178" y="236" width="18" height="12" rx="4"/>
|
|
22
|
+
<rect x="206" y="236" width="18" height="12" rx="4"/>
|
|
23
|
+
<rect x="234" y="236" width="18" height="12" rx="4"/>
|
|
24
|
+
<rect x="178" y="310" width="18" height="12" rx="4"/>
|
|
25
|
+
<rect x="206" y="310" width="18" height="12" rx="4"/>
|
|
26
|
+
<rect x="234" y="310" width="18" height="12" rx="4"/>
|
|
27
|
+
<rect x="178" y="384" width="18" height="12" rx="4"/>
|
|
28
|
+
<rect x="206" y="384" width="18" height="12" rx="4"/>
|
|
29
|
+
<rect x="234" y="384" width="18" height="12" rx="4"/>
|
|
30
|
+
</g>
|
|
31
|
+
<!-- The lit slot: up and serving. -->
|
|
32
|
+
<circle cx="322" cy="242" r="11" fill="#3ddc97"/>
|
|
33
|
+
</svg>
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "home-hosted",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.9.0",
|
|
5
5
|
"packageManager": "pnpm@12.5.1",
|
|
6
6
|
"description": "Self-hosted control panel that keeps your home-server processes alive: supervision, restarts, port conflicts, logs — and a UI you can replace.",
|
|
7
7
|
"author": "NamesMT <dangquoctrung123@gmail.com>",
|
|
@@ -62,6 +62,8 @@
|
|
|
62
62
|
"docs/REVERSE_PROXY.md",
|
|
63
63
|
"docs/SERVERS.md",
|
|
64
64
|
"docs/UI_CREATION.md",
|
|
65
|
+
"docs/branding/logo.svg",
|
|
66
|
+
"docs/branding/social-preview.png",
|
|
65
67
|
"uis/stock/dist"
|
|
66
68
|
],
|
|
67
69
|
"engines": {
|
|
@@ -90,40 +92,41 @@
|
|
|
90
92
|
"prepack": "pnpm run build",
|
|
91
93
|
"build:ui": "node scripts/build-uis.mjs stock",
|
|
92
94
|
"build:uis": "node scripts/build-uis.mjs --zip",
|
|
95
|
+
"brand": "node scripts/build-brand.mjs",
|
|
93
96
|
"media": "node scripts/capture-media.mjs"
|
|
94
97
|
},
|
|
95
98
|
"dependencies": {
|
|
96
99
|
"@grammyjs/auto-retry": "^2.0.2",
|
|
97
|
-
"@namesmt/utils": "^0.5.
|
|
98
|
-
"@scalar/hono-api-reference": "^0.12.
|
|
99
|
-
"@zip.js/zip.js": "^2.
|
|
100
|
-
"arktype": "^2.2.
|
|
100
|
+
"@namesmt/utils": "^0.5.21",
|
|
101
|
+
"@scalar/hono-api-reference": "^0.12.13",
|
|
102
|
+
"@zip.js/zip.js": "^2.24.0",
|
|
103
|
+
"arktype": "^2.2.8",
|
|
101
104
|
"citty": "^0.2.2",
|
|
102
105
|
"consola": "^3.4.2",
|
|
103
106
|
"grammy": "^1.46.0",
|
|
104
|
-
"hono": "^4.13.
|
|
105
|
-
"hono-openapi": "^1.3.
|
|
106
|
-
"srvx": "^1.0.
|
|
107
|
-
"std-env": "^4.
|
|
107
|
+
"hono": "^4.13.13",
|
|
108
|
+
"hono-openapi": "^1.3.5",
|
|
109
|
+
"srvx": "^1.0.6",
|
|
110
|
+
"std-env": "^4.3.0"
|
|
108
111
|
},
|
|
109
112
|
"devDependencies": {
|
|
110
113
|
"@antfu/eslint-config": "^9.5.1",
|
|
111
|
-
"@i18n-micro/vue": "^1.4.
|
|
114
|
+
"@i18n-micro/vue": "^1.4.10",
|
|
115
|
+
"@lucide/vue": "^1.59.0",
|
|
112
116
|
"@standard-schema/spec": "^1.1.0",
|
|
113
117
|
"@tailwindcss/vite": "^4.3.3",
|
|
114
|
-
"@types/node": "^24.
|
|
115
|
-
"@vitejs/plugin-vue": "^6.0.
|
|
116
|
-
"@vitest/coverage-v8": "^5.0.
|
|
118
|
+
"@types/node": "^24.19.2",
|
|
119
|
+
"@vitejs/plugin-vue": "^6.0.10",
|
|
120
|
+
"@vitest/coverage-v8": "^5.0.5",
|
|
117
121
|
"@vue/test-utils": "^2.5.1",
|
|
118
122
|
"clsx": "^2.1.1",
|
|
119
|
-
"eslint": "^10.
|
|
123
|
+
"eslint": "^10.13.0",
|
|
120
124
|
"gifenc": "^1.0.3",
|
|
121
|
-
"happy-dom": "^20.14.
|
|
122
|
-
"lint-staged": "^17.
|
|
123
|
-
"
|
|
124
|
-
"playwright": "^1.63.0",
|
|
125
|
+
"happy-dom": "^20.14.6",
|
|
126
|
+
"lint-staged": "^17.6.0",
|
|
127
|
+
"playwright": "^1.64.1",
|
|
125
128
|
"pngjs": "^7.0.0",
|
|
126
|
-
"reka-ui": "^2.
|
|
129
|
+
"reka-ui": "^2.11.0",
|
|
127
130
|
"simple-git-hooks": "^2.14.0",
|
|
128
131
|
"splitpanes": "^4.1.2",
|
|
129
132
|
"spreadsheet-i18n": "^0.3.12",
|
|
@@ -131,12 +134,12 @@
|
|
|
131
134
|
"tailwindcss": "^4.3.3",
|
|
132
135
|
"tsx": "^4.23.15",
|
|
133
136
|
"typescript": "^6.0.3",
|
|
134
|
-
"vite": "^8.3.
|
|
137
|
+
"vite": "^8.3.4",
|
|
135
138
|
"vite-plugin-vue-devtools": "^8.2.1",
|
|
136
|
-
"vitest": "^5.0.
|
|
137
|
-
"vue": "^3.5.
|
|
138
|
-
"vue-router": "^5.
|
|
139
|
-
"vue-tsc": "^3.3.
|
|
139
|
+
"vitest": "^5.0.5",
|
|
140
|
+
"vue": "^3.5.44",
|
|
141
|
+
"vue-router": "^5.4.0",
|
|
142
|
+
"vue-tsc": "^3.3.13"
|
|
140
143
|
},
|
|
141
144
|
"simple-git-hooks": {
|
|
142
145
|
"pre-commit": "pnpm lint-staged && node scripts/stamp-uis.mjs"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e={"activity.emptyTitle":`Keine Ereignisse in den letzten 24 Stunden`,"activity.emptyDescription":`Starts, Beendigungen, Abstürze und Wiederherstellungen erscheinen hier, sobald sie passieren.`,"activity.ran":`lief {duration}`,"addServer.title":`Server hinzufügen`,"addServer.description":`Hängt einen Eintrag an servers.config.json von {workspace} an; jede ausgelassene Richtlinie wird von den Standardwerten geerbt.`,"addServer.thisWorkspace":`diesem Arbeitsbereich`,"addServer.identity":`Identität`,"addServer.id":`Id`,"addServer.idHint":`Kleinbuchstaben, Ziffern, Bindestriche und Unterstriche; wird in URLs und Protokollen verwendet.`,"addServer.labelHint":`Wird im Panel angezeigt; standardmäßig die Id.`,"addServer.process":`Prozess`,"addServer.commandHint":`Wird ohne Shell gestartet.`,"addServer.argumentsHint":`Ein Argument pro Zeile; Leerzeichen innerhalb einer Zeile bleiben erhalten.`,"addServer.portLifecycle":`Port & Lebenszyklus`,"addServer.bindHint":`lan macht ihn im Netzwerk erreichbar; nur hinter einer Authentifizierung tun.`,"addServer.advanced":`Erweiterte Einstellungen`,"addServer.advancedHint":`Umgebung, Sicherungen, Richtlinie, Lebenszyklus, Bootstrap`,"addServer.backups":`Sicherungen`,"addServer.backupIgnore":`Bekannte generierte Dateien ignorieren`,"addServer.bootstrapDescription":`Ein einmaliger Befehl, der vor dem ersten Start ausgeführt wird.`,"addServer.bootstrapHint":`Installationen, Migrationen, Downloads — vor dem ersten Start, nicht bei jedem.`,"addServer.policyResources":`Richtlinie & Ressourcen`,"addServer.logBufferHint":`Leer behält den Standardwert.`,"addServer.inheritNote":`Neustart, Gesundheit und Stopp entsprechen zunächst {settings}; ändern Sie hier einen Wert, wird er in diesen Eintrag geschrieben, statt geerbt zu bleiben.`,"addServer.serverDefaults":`Einstellungen → Server-Standardwerte`,"addServer.placeholdersTitle":`Verfügbare Argument-Platzhalter`,"addServer.submit":`Zu servers.config.json hinzufügen`,"app.name":`home-hosted`,"app.tagline":`Kontrollpanel der Prozessüberwachung`,"bind.local":`local — 127.0.0.1`,"bind.lan":`lan — 0.0.0.0`,"bind.explicit":`{bind} — explizit`,"common.cancel":`Abbrechen`,"common.confirm":`Bestätigen`,"common.reset":`Zurücksetzen`,"common.sending":`Senden…`,"common.keepIt":`Beibehalten`,"common.remove":`Entfernen`,"common.close":`Schließen`,"common.save":`Speichern`,"common.saving":`Wird gespeichert…`,"common.discard":`Verwerfen`,"common.delete":`Löschen`,"common.edit":`Bearbeiten`,"common.add":`Hinzufügen`,"common.retry":`Wiederholen`,"common.copy":`Kopieren`,"common.copied":`Kopiert`,"common.enabled":`Aktiviert`,"common.disabled":`Deaktiviert`,"common.none":`Keine`,"common.unknown":`unbekannt`,"common.loading":`Wird geladen…`,"common.selectAll":`Alle auswählen`,"common.selectNone":`Nichts auswählen`,"common.name":`Name`,"common.label":`Bezeichnung`,"common.hostname":`Hostname`,"common.port":`Port`,"common.command":`Befehl`,"common.environment":`Umgebung`,"common.memory":`Speicher`,"common.cpu":`CPU`,"common.disk":`Speicherplatz`,"common.uptime":`Laufzeit`,"common.status":`Status`,"common.actions":`Aktionen`,"common.details":`Details`,"common.overview":`Übersicht`,"common.warning":`Warnung`,"common.error":`Fehler`,"common.confirmQuestion":`{label}?`,"common.unset":`nicht gesetzt`,"common.never":`nie`,"common.now":`gerade eben`,"common.dismiss":`Ausblenden`,"connection.live":`Live`,"connection.connecting":`Verbinden`,"connection.reconnecting":`Neu verbinden`,"connection.liveUpdates":`Live-Aktualisierungen`,"editor.title":`Konfiguration bearbeiten`,"editor.description":`Schreibt Überschreibungen nach {file}; alles Unberührte erbt weiter die Standardwerte.`,"editor.inSync":`synchron`,"editor.process":`Prozess`,"editor.processDescription":`Was gestartet wird, wo und mit welcher Umgebung.`,"editor.labelPlaceholder":`fällt auf die Id zurück`,"editor.argumentsHint":`Eines pro Zeile; {port} {host} {home} {projectDir} {dataRoot} {id} {label} {cwd} {bind} {lanIp} werden ersetzt.`,"editor.portTitle":`Port & Bindung`,"editor.portDescription":`Wie der Port vorab geprüft wird und wo der Prozess erreichbar ist.`,"editor.bindHint":`Eine eigene IPv4, die die Konfiguration bereits setzt, bleibt auswählbar.`,"editor.lifecycle":`Lebenszyklus`,"editor.lifecycleDescription":`Ob der Supervisor diesen Eintrag überhaupt besitzt.`,"editor.save":`In servers.config.json speichern`,"editor.changesTitle":`Ungespeicherte Änderungen`,"editor.changesDescription":`Was Speichern in servers.config.json schreiben würde.`,"editor.nothingToSave":`Nichts zu speichern.`,"health.disabled":`Nicht geprüft`,"health.unknown":`Nicht getestet`,"health.healthy":`Gesund`,"health.unhealthy":`Ungesund`,"history.start":`Gestartet`,"history.exit":`Beendet`,"history.crash":`Abgestürzt`,"history.forcedRestart":`Erzwungener Neustart`,"history.unhealthy":`Ungesund`,"history.recovered":`Wiederhergestellt`,"host.loadPerCpu":`Last / cpu`,"host.cpuTemp":`CPU-Temp.`,"host.samplingOff":`Host-Sampling ist aus`,"host.samplingOffDescription":`Aktivieren Sie es in den Einstellungen, um Last, Speicher, Speicherplatz und Temperatur zu erfassen.`,"host.swap":`Swap`,"host.cpus":`CPUs`,"host.loadAverage":`Last 1 / 5 / 15`,"host.memoryUsed":`Speicher belegt`,"host.sampled":`Erfasst`,"host.filesystems":`Dateisysteme`,"host.thresholdsBreached":`Schwellenwerte überschritten`,"killPort.trigger":`Beenden, was Port {port} belegt`,"killPort.title":`Den Prozess auf Port {port} beenden?`,"killPort.body":`Er wird zuerst zum Stoppen aufgefordert und nur beendet, wenn er das ignoriert. Alles, was dieses Panel überwacht, wird abgelehnt, ein verwalteter Server kann so nicht verloren gehen.`,"killPort.confirm":`Beenden`,"language.label":`Sprache`,"language.select":`Sprache wählen`,"lifecycle.restartTitle":`Neustartrichtlinie`,"lifecycle.restartDescription":`Backoff zwischen den Versuchen nach einem Absturz.`,"lifecycle.restartOnCrash":`Neustart nach Absturz`,"lifecycle.maxRetries":`Max. Wiederholungen`,"lifecycle.maxRetriesHint":`Versuche, bevor der Server abgestürzt belassen wird.`,"lifecycle.baseDelay":`Basisverzögerung (ms)`,"lifecycle.factor":`Backoff-Faktor`,"lifecycle.maxDelay":`Max. Verzögerung (ms)`,"lifecycle.resetAfter":`Zurücksetzen nach (ms)`,"lifecycle.resetAfterHint":`Ein Lauf dieser Länge setzt den Wiederholungszähler zurück.`,"lifecycle.healthTitle":`Gesundheitsprüfung`,"lifecycle.healthDescription":`Wie die Bereitschaft geprüft wird, sobald der Prozess läuft.`,"lifecycle.probe":`Diesen Server prüfen`,"lifecycle.probeType":`Prüftyp`,"lifecycle.probePort":`TCP-Port — nur Verbindung`,"lifecycle.probeHttp":`HTTP-Anfrage — Antwort prüfen`,"lifecycle.interval":`Intervall (ms)`,"lifecycle.timeout":`Timeout (ms)`,"lifecycle.unhealthyAfter":`Ungesund nach`,"lifecycle.unhealthyAfterHint":`Aufeinanderfolgende fehlgeschlagene Prüfungen.`,"lifecycle.forceRestartAfter":`Erzwungener Neustart nach (ms)`,"lifecycle.forceRestartAfterHint":`0 deaktiviert es.`,"lifecycle.startTimeout":`Start-Timeout (ms)`,"lifecycle.httpScheme":`Schema`,"lifecycle.allowInvalidCert":`Beliebiges Zertifikat akzeptieren`,"lifecycle.allowInvalidCertHint":`Für ein selbstsigniertes Backend. Nur bei https sinnvoll.`,"lifecycle.httpPath":`HTTP-Pfad`,"lifecycle.httpMethod":`HTTP-Methode`,"lifecycle.expectStatus":`Status erwarten`,"lifecycle.expectStatusHint":`Leer akzeptiert alles darunter.`,"lifecycle.expectStatusBelow":`Gesund unter Status`,"lifecycle.expectBody":`Body muss enthalten`,"lifecycle.expectBodyPlaceholder":`optionaler Teilstring`,"lifecycle.headBodyHint":`HEAD hat keinen Antwort-Body, daher wird diese Prüfung ignoriert — verwenden Sie GET oder leeren Sie sie.`,"lifecycle.stopTitle":`Stoppverhalten`,"lifecycle.stopDescription":`Wie der Prozessbaum zum Beenden aufgefordert wird.`,"lifecycle.signal":`Signal`,"lifecycle.signalTerm":`SIGTERM — zum Stoppen auffordern`,"lifecycle.signalInt":`SIGINT — unterbrechen (Strg-C)`,"lifecycle.signalKill":`SIGKILL — sofort beenden`,"lifecycle.grace":`Karenzzeit (ms)`,"lifecycle.killGroup":`Die ganze Prozessgruppe beenden`,"lifecycle.killPortHolders":`Auch beenden, was den Port belegt`,"login.title":`Anmelden, um fortzufahren`,"login.firstRunTitle":`Dieses Panel hat noch kein Passwort`,"login.firstRunSubtitle":`Alles auf dieser Maschine kann darauf zugreifen. Setzen Sie direkt nach der Anmeldung ein Passwort.`,"login.authOffTitle":`Die Authentifizierung ist ausgeschaltet`,"login.authOffSubtitle":`Das Panel lauscht nur auf Loopback — das ist derzeit der einzige Schutz.`,"login.subtitle":`Das Kontrollpanel ist geschützt. Geben Sie das Passwort ein, um fortzufahren.`,"login.password":`Passwort`,"login.showPassword":`Passwort anzeigen`,"login.hidePassword":`Passwort verbergen`,"login.signIn":`Anmelden`,"login.openDashboard":`Dashboard öffnen`,"login.exposed":`Exponiert`,"login.defaultPasswordWarning":`Noch das Standardpasswort {password} aktiv. Ändern Sie es in den Einstellungen — ein Lauschen jenseits von 127.0.0.1 wird bis dahin abgelehnt.`,"login.noPasswordWarning":`Es ist kein Passwort gesetzt. Öffnen Sie die Einstellungen auf dieser Maschine, um eines hinzuzufügen, oder führen Sie {command} aus.`,"login.sessionNote":`Sitzungen bleiben {days} Tage nach dem letzten Besuch angemeldet — jede Anfrage verlängert dieses Fenster. Abmelden betrifft nur diesen Browser.`,"login.servedFromThisMachine":`von dieser Maschine ausgeliefert`,"logs.emptyHint":`Noch keine Ausgabe.`,"logs.noMatches":`Nichts passt zu dieser Suche oder diesem Stream-Filter.`,"logs.searchPlaceholder":`Diese Ausgabe durchsuchen`,"logs.searchLabel":`Die Protokollausgabe durchsuchen`,"logs.clearSearch":`Die Suche leeren`,"logs.prevMatch":`Vorheriger Treffer`,"logs.nextMatch":`Nächster Treffer`,"logs.showTimestamps":`Zeitstempel anzeigen`,"logs.hideTimestamps":`Zeitstempel ausblenden`,"logs.timestampsShort":`ts`,"logs.pause":`Pause`,"logs.follow":`Folgen`,"logs.clearHint":`Den Puffer im Speicher leeren`,"logs.clearLabel":`Den Protokollpuffer leeren`,"logs.downloadHint":`Die gespeicherte Datei herunterladen`,"logs.downloadLabel":`Die Protokolldatei herunterladen`,"logs.outputLabel":`Protokollausgabe`,"logs.jumpToLatest":`Zum Neuesten springen`,"logs.streaming":`Streaming`,"logs.paused":`pausiert`,"logs.snapshot":`Momentaufnahme`,"logs.linesOf":`{count} von {total} Zeilen`,"logs.matches":`{count} Treffer`,"logs.rowHeight":`Zeilenhöhe {height}px · gefenstert`,"logs.streamAll":`Alle`,"logs.streamOut":`out`,"logs.streamErr":`err`,"logs.streamSys":`sys`,"metric.emptyHint":`sammle…`,"nav.global":`Global`,"nav.workspace":`Arbeitsbereich`,"nav.others":`Sonstiges`,"nav.overview":`Übersicht`,"nav.servers":`Server`,"nav.logs":`Protokolle`,"nav.workspaceSettings":`Arbeitsbereich-Einstellungen`,"nav.globalOverview":`Globale Übersicht`,"nav.globalSettings":`Globale Einstellungen`,"nav.reverseProxy":`Reverse-Proxy`,"nav.navigation":`Navigation`,"nav.closeNavigation":`Navigation schließen`,"nav.hostVitals":`Host-Werte`,"notice.configError":`Die Konfiguration konnte nicht gelesen werden: {error}`,"notice.configErrorHint":`Das Panel läuft weiter mit der bisherigen Konfiguration; korrigieren Sie {path}, dann lädt es sie selbst neu.`,"notice.defaultPassword":`Dieses Panel nutzt noch sein Standardpasswort.`,"notice.defaultPasswordHint":`Ändern Sie es in den globalen Einstellungen — ein Lauschen jenseits von 127.0.0.1 wird bis dahin abgelehnt.`,"notice.noPassword":`Die Authentifizierung ist an, aber es ist kein Passwort gesetzt, also wird nichts verlangt.`,"notice.noPasswordHint":`Setzen Sie eines in den globalen Einstellungen, um das Panel wirklich zu sperren.`,"notice.exposed":`Dieses Panel ist über 127.0.0.1 hinaus erreichbar.`,"notice.exposedHint":`Halten Sie das Passwort stark, oder setzen Sie einen TLS-terminierenden Proxy davor.`,"notice.authOff":`Die Authentifizierung ist deaktiviert — das Panel ist nur durch seine Loopback-Bindung geschützt.`,"onPortConflict.block":`block — Start verweigern`,"onPortConflict.warn":`warn — trotzdem starten`,"onPortConflict.follow":`follow — einen abgekoppelten Neustart seiner selbst übernehmen`,"onPortConflict.reclaim":`reclaim — durch eine überwachte Kopie ersetzen`,"onPortConflict.kill":`kill — beenden, was den Port belegt`,"palette.placeholder":`Zu einem Server springen, Aktion ausführen, Einstellung öffnen…`,"palette.openServer":`{label} öffnen`,"palette.serverLogs":`Protokolle von {label}`,"palette.startServer":`{label} starten`,"palette.stopServer":`{label} stoppen`,"palette.restartServer":`{label} neu starten`,"palette.goOverview":`Zur Übersicht`,"palette.goGlobalOverview":`Zur globalen Übersicht`,"palette.goGlobalSettings":`Zu den globalen Einstellungen`,"palette.goProxy":`Zum Reverse-Proxy`,"palette.goServers":`Zu den Servern`,"palette.goLogs":`Zu den Protokollen`,"palette.goWorkspaceSettings":`Zu den Arbeitsbereich-Einstellungen`,"palette.startAll":`Alle aktivierten Server hier starten`,"palette.stopAll":`Alle Server hier stoppen`,"palette.switchWorkspace":`Zu {label} wechseln`,"palette.globalSetting":`Globale Einstellung: {label}`,"palette.workspaceSetting":`Arbeitsbereich-Einstellung: {label}`,"palette.theme":`Design {theme}`,"palette.groupServers":`Server`,"palette.groupActions":`Aktionen`,"palette.groupNavigate":`Navigation`,"palette.groupWorkspaces":`Arbeitsbereiche`,"palette.groupAppearance":`Erscheinungsbild`,"palette.navigate":`navigieren`,"palette.run":`ausführen`,"palette.noMatches":`Nichts passt zu „{query}“.`,"proxy.account.automatic":`Automatisch (das einzige Konto, das antworten kann)`,"proxy.account.noTxt":`keine TXT-Unterstützung`,"proxy.account.noCredentials":`keine Zugangsdaten`,"proxy.account.gone":`Dieses Konto ist nicht mehr konfiguriert.`,"proxy.account.emptyHint":`DNS-01 schreibt den Challenge-Eintrag hierher. Leer nutzt das einzige Konto, das eine Challenge beantworten kann, sofern es eines gibt.`,"proxy.account.cannotWrite":`Ein {provider}-Konto kann keine TXT-Einträge schreiben, kann also keine DNS-01-Challenge beantworten.`,"proxy.account.noCredentialsYet":`Für dieses {provider}-Konto sind noch keine Zugangsdaten gespeichert.`,"proxy.account.through":`Die Challenge wird über dieses {provider}-Konto geschrieben.`,"proxy.certificate.title":`Hochgeladene Zertifikate`,"proxy.certificate.description":`Ausgeliefert von Routen, die auf „Hochgeladenes Zertifikat“ stehen. Alles andere — öffentlich oder lokal — wird von der Engine selbst zertifiziert.`,"proxy.certificate.add":`Zertifikat hinzufügen`,"proxy.certificate.emptyTitle":`Kein Zertifikat hochgeladen`,"proxy.certificate.emptyDescription":`Eine Route auf „Hochgeladenes Zertifikat“ braucht ein Paar, das ihren Hostnamen abdeckt, sonst hat die Engine nichts, was sie dafür ausliefern könnte.`,"proxy.certificate.daysLeft":`{days} Tage übrig`,"proxy.certificate.subject":`Subjekt`,"proxy.certificate.issuer":`Aussteller`,"proxy.certificate.validUntil":`Gültig bis`,"proxy.certificate.covers":`Deckt ab`,"proxy.certificate.noSans":`keine Hostnamen in ihren SANs`,"proxy.certificate.removeAria":`{name} entfernen`,"proxy.certificate.removeQuestion":`„{name}“ entfernen?`,"proxy.certificate.removeBody":`Das Paar wird von der Platte gelöscht. Eine Route, die weiter auf „Hochgeladenes Zertifikat“ steht, liefert weiter das eigene Zertifikat der Engine aus, bis Sie das ändern, und das Panel verweigert dies, solange eine sie noch nutzt.`,"proxy.certificate.removeIt":`Entfernen`,"proxy.certificate.stored":`„{label}“ gespeichert.`,"proxy.certificate.removed":`„{name}“ entfernt.`,"proxy.certificateDialog.title":`Ein Zertifikat hinzufügen`,"proxy.certificateDialog.description":`Das Paar wird unter seiner Id auf der Platte gespeichert, und eine Route auf „Hochgeladenes Zertifikat“ kann es ausliefern.`,"proxy.certificateDialog.label":`Bezeichnung`,"proxy.certificateDialog.labelPlaceholder":`Heim-Wildcard`,"proxy.certificateDialog.id":`Id`,"proxy.certificateDialog.idHint":`benennt die Dateien auf der Platte`,"proxy.certificateDialog.certFile":`Zertifikatsdatei (.pem, .crt)`,"proxy.certificateDialog.keyFile":`Datei mit privatem Schlüssel (.pem, .key)`,"proxy.certificateDialog.pasteCert":`Oder das Zertifikat einfügen`,"proxy.certificateDialog.pasteKey":`Oder den privaten Schlüssel einfügen`,"proxy.certificateDialog.certPlaceholder":`-----BEGIN CERTIFICATE-----`,"proxy.certificateDialog.keyPlaceholder":`-----BEGIN PRIVATE KEY-----`,"proxy.certificateDialog.store":`Das Zertifikat speichern`,"proxy.certificateDialog.errorLabel":`eine Bezeichnung ist erforderlich`,"proxy.certificateDialog.errorId":`die Id muss mit einem Buchstaben oder einer Ziffer beginnen, danach Buchstaben, Ziffern, Bindestrich oder Unterstrich`,"proxy.certificateDialog.errorTaken":`„{id}“ wird bereits von einem anderen Zertifikat verwendet`,"proxy.certificateDialog.errorCertificate":`das Zertifikat einfügen oder wählen`,"proxy.certificateDialog.errorPrivateKey":`den privaten Schlüssel einfügen oder wählen`,"proxy.engine.title":`Engine`,"proxy.engine.description":`Der Reverse-Proxy selbst. Das Panel lädt den aktuellen Build herunter, überwacht ihn und wendet seine Konfiguration über die eigene Admin-API der Engine an.`,"proxy.engine.notInFrame":`Der Proxy-Zustand liegt noch nicht im Frame dieses Panels.`,"proxy.engine.readFailed":`Die Engine konnte nicht gelesen werden`,"proxy.engine.notInstalled":`Keine Engine installiert`,"proxy.engine.installNote":`Die Installation lädt die aktuelle {engine}-Version herunter, die das Panel überwacht — etwa 46 MB, einmalig. Nichts wird exponiert, bis eine Route hinzugefügt und der Proxy eingeschaltet wird.`,"proxy.engine.version":`Version`,"proxy.engine.notProbed":`noch nicht geprüft`,"proxy.engine.source":`Quelle`,"proxy.engine.sourceCustom":`ein von Hand konfigurierter Pfad`,"proxy.engine.sourceDownloaded":`vom Panel heruntergeladen`,"proxy.engine.size":`Größe`,"proxy.engine.binary":`Binärdatei`,"proxy.engine.copyPath":`Den Pfad der Binärdatei kopieren`,"proxy.engine.address":`Live-Adresse`,"proxy.engine.process":`Prozess`,"proxy.engine.certificates":`Zertifikate`,"proxy.engine.noCert":`noch kein öffentliches Zertifikat`,"proxy.engine.expiresSoon":`das früheste läuft in {days} Tagen ab`,"proxy.engine.acme":`ACME-Zertifikate`,"proxy.engine.localCa":`Lokale CA`,"proxy.engine.dns01":`DNS-01`,"proxy.engine.tcpUdp":`TCP/UDP`,"proxy.engine.documentation":`{engine}-Dokumentation`,"proxy.engine.failed":`Die Engine hat einen Fehler gemeldet`,"proxy.engine.setcapHint":`Ein privilegierter Port (unter 1024) braucht das unter Linux einmalig:`,"proxy.engine.copyCommand":`Den Befehl kopieren`,"proxy.engine.install":`Engine installieren`,"proxy.engine.start":`Starten`,"proxy.engine.stop":`Stoppen`,"proxy.engine.apply":`Konfiguration anwenden`,"proxy.engine.revert":`Zurücknehmen`,"proxy.engine.revertHint":`Zur Konfiguration zurückkehren, die vor der letzten galt`,"proxy.engine.update":`Engine aktualisieren`,"proxy.listener.title":`Listener & Zertifikate`,"proxy.listener.description":`Wo der Proxy lauscht. 80 und 443 sind das, was ein Router weiterleitet und was eine ACME-Challenge erwartet; ein Port unter 1024 braucht eine privilegierte Bindung.`,"proxy.listener.unprivilegedPorts":`unprivilegierte Ports`,"proxy.listener.run":`Den Reverse-Proxy ausführen`,"proxy.listener.runHint":`Wird mit Einstellungen speichern gespeichert; das Panel startet oder stoppt die Engine entsprechend.`,"proxy.listener.httpPort":`HTTP-Port`,"proxy.listener.httpPortHint":`ACME-HTTP-01-Challenges und die Umleitung auf HTTPS.`,"proxy.listener.httpsPort":`HTTPS-Port`,"proxy.listener.httpsPortHint":`Wo Zertifikate ausgeliefert werden.`,"proxy.listener.useFallback":`{http} / {https} verwenden`,"proxy.listener.useStandard":`80 / 443 verwenden`,"proxy.listener.forwardHint":`Läuft unprivilegiert — leiten Sie 80 und 443 auf dem Router auf diese Ports weiter.`,"proxy.listener.privilegedTitle":`Ein Port unter 1024 braucht eine privilegierte Bindung`,"proxy.listener.privilegedBody":`Wenn das Panel als Ihr eigener Benutzer läuft, kann es 80 oder 443 nicht ohne Berechtigung binden. Erteilen Sie sie einmalig {how} und starten Sie die Engine neu, oder behalten Sie {http}/{https} und leiten Sie 80/443 vom Router weiter. Unter Windows ist der Port frei bindbar, aber etwas anderes könnte ihn belegen.`,"proxy.listener.privilegedWithSetcap":`mit {command} für die Engine-Binärdatei`,"proxy.listener.email":`E-Mail des ACME-Kontos`,"proxy.listener.emailPlaceholder":`you@example.com`,"proxy.listener.emailHint":`Let's Encrypt nutzt sie, um Sie vor einem ablaufenden Zertifikat zu warnen. Ein rein lokaler Name bekommt die eigene CA der Engine und braucht keine.`,"proxy.listener.emailNeededTitle":`Für automatisches HTTPS ist eine E-Mail nötig`,"proxy.listener.emailNeededBody":`„{host}“ ist ein öffentlicher Hostname, also fragt die Engine eine CA nach einem Zertifikat, und die Kontoadresse ist erforderlich. Tragen Sie sie ein, schalten Sie TLS für die Route aus, oder aktivieren Sie den Staging-Endpunkt, während Sie Dinge ausprobieren.`,"proxy.listener.staging":`Den ACME-Staging-Endpunkt verwenden`,"proxy.listener.stagingHint":`Nicht vertrauenswürdige Zertifikate und kein Aufbrauchen des Ratenlimits, während Sie Dinge ausprobieren.`,"proxy.listener.dns01":`Challenges mit DNS-01 beantworten`,"proxy.listener.dns01Hint":`Das Panel schreibt den Challenge-Eintrag über das DNS-Konto einer Route, daher ist kein eingehender Port nötig, um ein Zertifikat zu bekommen.`,"proxy.listener.resolvers":`Den Eintrag abgleichen gegen`,"proxy.listener.resolversPlaceholder":`1.1.1.1, 8.8.8.8`,"proxy.listener.resolversHint":`Nameserver, von denen die Engine den Eintrag liest; leer nutzt ihre eigenen. Nötig, wo Split-Horizon-DNS ihr eine andere Sicht zeigt.`,"proxy.listener.noAccountTitle":`Kein DNS-Konto kann eine Challenge beantworten`,"proxy.listener.noAccountBody":`DNS-01 ist an, aber kein Konto hat dafür nutzbare Zugangsdaten. Fügen Sie eines unter Arbeitsbereich-Einstellungen → Dynamic DNS hinzu, sonst bleiben öffentliche Namen auf den Port-Challenges und warten auf eine CA, die sie nicht erreichen kann.`,"proxy.listener.manyAccountsTitle":`Jede öffentliche Route muss ein Konto wählen`,"proxy.listener.manyAccountsBody":`Mehr als ein Konto kann antworten, also kann „Automatisch“ nicht wählen. Wählen Sie eines pro Route, sonst bleiben diese Namen auf den Port-Challenges.`,"proxy.listener.accountNote":`Jede Route wählt ihr eigenes Konto, daher bekommt jeder Name in einer anderen Zone das richtige. Eine Route auf „Automatisch“ nutzt das einzige Konto, das die Tabelle nennt. Ein Name, den kein Konto beantwortet, bleibt auf den Port-Challenges.`,"proxy.listener.pendingNote":`Ein öffentlicher Name, dessen Zertifikat noch ausgestellt wird, ist in der Zwischenzeit erreichbar — über HTTPS mit einem vorübergehend nicht vertrauenswürdigen Zertifikat oder über reines HTTP mit einer Seite, die die weiterzuleitenden Ports nennt.`,"proxy.retry.inFallback":`~8 Stunden`,"proxy.retryButton.force":`Zertifikat erneut erzwingen`,"proxy.retryButton.question":`Die CA erneut für {host} fragen?`,"proxy.retryButton.body":`Die Engine konnte zuvor kein CA-Zertifikat für diesen Hostnamen erhalten und nutzt ihre lokale CA. Sie versucht es automatisch alle {retryIn} erneut.`,"proxy.retryButton.warning":`Ein erneuter Versuch beendet diese Route und macht sie eine Weile unerreichbar — sind Sie sicher, dass Sie fortfahren möchten?`,"proxy.retryButton.leave":`Lassen`,"proxy.retryButton.now":`Jetzt erneut versuchen`,"proxy.routeDialog.titleEdit":`Route bearbeiten`,"proxy.routeDialog.titleAdd":`Route hinzufügen`,"proxy.routeDialog.description":`Ein Hostname, wohin er weiterleitet und wie er über TLS ausgeliefert wird.`,"proxy.routeDialog.hostname":`Hostname`,"proxy.routeDialog.hostnamePlaceholder":`gitea.example.com`,"proxy.routeDialog.path":`Pfadpräfix`,"proxy.routeDialog.pathPlaceholder":`/optional`,"proxy.routeDialog.pathHint":`Leer bedient den ganzen Host.`,"proxy.routeDialog.target":`Ziel`,"proxy.routeDialog.tls":`TLS`,"proxy.routeDialog.dnsAccount":`DNS-Konto`,"proxy.routeDialog.localNote":`Ein rein lokaler Name wird von der eigenen CA der Engine signiert, daher ist kein DNS-Konto beteiligt.`,"proxy.routeDialog.workspace":`Arbeitsbereich`,"proxy.routeDialog.server":`Server`,"proxy.routeDialog.noServers":`dieser Arbeitsbereich hat keine Server`,"proxy.routeDialog.stoppedNote":`Ein gestoppter Server wird nicht geroutet: Die Zeile zeigt „Kein Upstream“, bis er wieder läuft.`,"proxy.routeDialog.upstream":`Upstream`,"proxy.routeDialog.upstreamPlaceholder":`http://10.0.0.5:8080`,"proxy.routeDialog.upstreamHint":`Eine wörtliche Adresse, einschließlich Port. Ein Schema ist optional.`,"proxy.routeDialog.panelNote":`Das Kontrollpanel auszuliefern braucht eingeschaltete Authentifizierung und ein Passwort, das nicht das Standardpasswort ist — sonst verweigert das Panel das Speichern.`,"proxy.routeDialog.save":`Route speichern`,"proxy.routes.title":`Routen`,"proxy.routes.description":`Ein öffentlicher Hostname pro Dienst. Eine Route kann auf einen überwachten Server in einem beliebigen Arbeitsbereich zeigen, auf dieses Panel oder auf einen wörtlichen Upstream.`,"proxy.routes.add":`Route hinzufügen`,"proxy.routes.cannotSaveTitle":`Diese Routen können so nicht gespeichert werden`,"proxy.routes.cannotRoute":`{host} kann nicht geroutet werden`,"proxy.routes.emptyTitle":`Noch keine Routen`,"proxy.routes.emptyDescription":`Fügen Sie eine hinzu, um einen Server über eine Domain zu exponieren — die Engine holt das Zertifikat von selbst.`,"proxy.routes.addFirst":`Die erste Route hinzufügen`,"proxy.routes.columnHostname":`Hostname`,"proxy.routes.columnTarget":`Ziel`,"proxy.routes.columnUpstream":`Upstream`,"proxy.routes.columnTls":`TLS`,"proxy.routes.columnState":`Zustand`,"proxy.routes.columnOn":`An`,"proxy.routes.openInTab":`{url} in einem neuen Tab öffnen`,"proxy.routes.noHostname":`noch kein Hostname`,"proxy.routes.toggle":`Route {name}`,"proxy.routes.edit":`{name} bearbeiten`,"proxy.routes.remove":`{name} entfernen`,"proxy.status.certified":`Zertifikat bereit`,"proxy.status.localCa":`Engine-CA`,"proxy.status.uploaded":`hochgeladenes Paar`,"proxy.status.awaitingCa":`wartet auf die CA`,"proxy.status.failed":`Zertifikat fehlgeschlagen`,"proxy.status.fellBack":`Auf lokale CA zurückgefallen`,"proxy.status.serving":`Wird ausgeliefert`,"proxy.status.disabled":`Deaktiviert`,"proxy.status.noUpstream":`Kein Upstream`,"proxy.status.error":`Fehler`,"proxy.status.unapplied":`Noch nicht angewendet`,"proxy.status.off":`Aus`,"proxy.status.stopped":`Gestoppt`,"proxy.status.starting":`Startet`,"proxy.status.running":`Läuft`,"proxy.status.unsupported":`Nicht unterstützt`,"proxy.target.server":`Ein überwachter Server`,"proxy.target.panel":`Dieses Kontrollpanel`,"proxy.target.external":`Ein wörtlicher Upstream`,"proxy.target.controlPanel":`Kontrollpanel`,"proxy.target.noServer":`kein Server gewählt`,"proxy.target.noUpstream":`kein Upstream`,"proxy.tls.auto":`Automatisch — die Engine entscheidet`,"proxy.tls.off":`Aus — reines HTTP`,"proxy.tls.manual":`Hochgeladenes Zertifikat`,"proxy.tls.plain":`reines HTTP`,"proxy.tls.uploaded":`hochgeladenes Zertifikat`,"proxy.tls.letsEncrypt":`Let’s Encrypt`,"proxy.tls.engineCa":`Engine-CA (lokal)`,"proxy.tls.hintOff":`Reines HTTP: Für diesen Hostnamen wird kein Zertifikat ausgeliefert.`,"proxy.tls.hintManual":`Liefert das unten hochgeladene Zertifikatspaar aus.`,"proxy.tls.hintEmpty":`Ein öffentlicher Hostname bekommt ein ACME-Zertifikat; ein rein lokaler Name bekommt die eigene CA der Engine.`,"proxy.tls.hintPublic":`Ein öffentlicher Hostname, also fragt die Engine eine CA nach einem Zertifikat — eine E-Mail für das ACME-Konto ist nötig.`,"proxy.tls.hintLocal":`Ein rein lokaler Name, also signiert die Engine ihn mit ihrer eigenen, lokal vertrauenswürdigen CA.`,"proxy.validation.labelRequired":`eine Bezeichnung ist erforderlich`,"proxy.validation.idPattern":`die Id muss mit einem Buchstaben oder einer Ziffer beginnen, danach Buchstaben, Ziffern, Bindestrich oder Unterstrich`,"proxy.validation.hostRequired":`ein Hostname ist erforderlich`,"proxy.validation.hostPattern":`ein Hostname aus Buchstaben, Ziffern, Punkten und Bindestrichen, z. B. home.example.com`,"proxy.validation.hostTaken":`„{host}“ ist für diesen Pfad bereits geroutet`,"proxy.validation.pathSlash":`ein Pfadpräfix beginnt mit /`,"proxy.validation.pathSpace":`ein Pfadpräfix darf kein Leerzeichen enthalten`,"proxy.validation.idTaken":`Routen-Id „{id}“ wird zweimal verwendet`,"proxy.validation.workspaceRequired":`wählen Sie den Arbeitsbereich, dem der Server gehört`,"proxy.validation.serverRequired":`wählen Sie einen Server in diesem Arbeitsbereich`,"proxy.validation.url":`ein Upstream wie http://10.0.0.5:8080`,"proxy.view.title":`Reverse-Proxy`,"proxy.view.description":`Exponieren Sie Ihre Server über Domains, mit automatischem HTTPS über 80/443 oder eine DNS-Challenge.`,"proxy.view.dnsNote":`Für DNS-01 fügen Sie den DNS-Anbieter der Domain in einem beliebigen Arbeitsbereich unter Arbeitsbereich-Einstellungen → Dynamic DNS hinzu; eine Route kann dieses Konto dann wählen, aus jedem Arbeitsbereich.`,"proxy.view.readFailed":`Der Reverse-Proxy konnte nicht gelesen werden`,"proxy.view.reading":`Reverse-Proxy wird gelesen…`,"proxy.view.engineInstalled":`Engine installiert`,"proxy.view.engineStarting":`Engine startet`,"proxy.view.engineStopping":`Engine stoppt`,"proxy.view.configApplied":`Konfiguration angewendet`,"proxy.view.configReverted":`Konfiguration zurückgenommen`,"proxy.view.settingsSaved":`Einstellungen gespeichert.`,"proxy.view.settingsSavedToast":`Einstellungen gespeichert`,"proxy.view.retryFailed":`der erneute Versuch ist fehlgeschlagen`,"proxy.view.retryAsked":`die CA wird erneut für {host} gefragt`,"proxy.view.fieldChanged":`Feld geändert`,"proxy.view.fieldsChanged":`Felder geändert`,"proxy.view.noChanges":`Keine ungespeicherten Änderungen`,"proxy.view.discard":`Änderungen verwerfen`,"proxy.view.save":`Einstellungen speichern`,"serverCard.open":`{url} öffnen`,"serverCard.adoptedHint":`Dieser Prozess hat sich selbst ersetzt: Das Panel hat den Nachfolger übernommen, statt eine zweite Kopie zu starten. Stopp funktioniert weiterhin, seine Ausgabe bleibt dort, wohin der Nachfolger sie geschickt hat.`,"serverCard.detached":`abgekoppelt`,"serverCard.persistentHint":"Dieser Eintrag läuft unter seiner eigenen Nanny und läuft deshalb weiter, wenn das Panel stoppt, neu startet oder beendet wird. `home-hosted down` meldet ihn, statt ihn zu stoppen; nur ein ausdrücklicher Stopp tut das.","serverCard.persistent":`persistent`,"serverCard.disabled":`deaktiviert`,"serverCard.autostart":`Autostart`,"serverCard.healthProbe":`Gesundheitsprüfung`,"serverCard.notProbed":`nicht geprüft`,"serverCard.restarts":`Neustarts`,"serverCard.pid":`PID`,"serverCard.rss":`RSS`,"serverCard.procs":`Prozesse`,"serverCard.portFree":`frei`,"serverCard.portInUse":`belegt`,"serverCard.uptime24h":`24 h Laufzeit`,"serverCard.probe":`Prüfung`,"serverCard.crashes24h":`Abstürze 24 h`,"serverCard.lastCrash":`Letzter Absturz`,"serverCard.unhealthyFor":`Seit {duration} ungesund`,"serverCard.nextAttempt":`Nächster Versuch {when}`,"serverCard.start":`Starten`,"serverCard.stop":`Stoppen`,"serverCard.restart":`Neu starten`,"serverCard.detailsHint":`Live-Protokolle und vollständige Telemetrie`,"serverCard.moreActions":`Weitere Aktionen für {label}`,"serverCard.disable":`Server deaktivieren`,"serverCard.enable":`Server aktivieren`,"serverCard.removeFromAutostart":`Aus dem Autostart entfernen`,"serverCard.autostartWithPanel":`Autostart mit dem Panel`,"serverCard.clearLogBuffer":`Protokollpuffer leeren`,"serverCard.removeServer":`Server entfernen`,"serverCard.removeTitle":`Diesen Server entfernen?`,"serverCard.removeDescription":`{label} wird aus servers.config.json entfernt: sein Prozess wird gestoppt, und seine Protokolle und Absturzhistorie werden mit ihm gelöscht.`,"serverCard.statusField":`Status`,"serverForm.workingDirectory":`Arbeitsverzeichnis`,"serverForm.cwdHint":`Relative Pfade werden unter projectDir aufgelöst.`,"serverForm.arguments":`Argumente`,"serverForm.portHint":`Leer bedeutet, dass der Server keinen Port zum Prüfen oder für den Vorab-Check hat.`,"serverForm.bindAddress":`Bind-Adresse`,"serverForm.autostart":`Autostart mit up`,"serverForm.autostartHint":`Starten, sobald die Steuerungsebene hochkommt.`,"serverForm.persistent":`Persistent`,"serverForm.persistentHint":"Läuft weiter, wenn das Panel stoppt; `down` meldet ihn, statt ihn zu stoppen.","serverForm.envHint":`KEY=value pro Zeile, an den Prozess exportiert.`,"serverForm.dataEnvs":`Daten-Envs`,"serverForm.dataEnvsHint":`ENV=pfad pro Zeile — an den Prozess exportiert und automatisch gesichert.`,"serverForm.envFile":`Env-Datei`,"serverForm.envFilePlaceholder":`optionale KEY=value-Datei`,"serverForm.envFileHint":`Wird beim Start geladen; ihre Werte überschreiben env.`,"serverForm.dependsOn":`Hängt ab von`,"serverForm.dependsOnHint":`Kommagetrennt; zuerst gestartet und auf Gesundheit gewartet, zuletzt gestoppt.`,"serverForm.backupPaths":`Zusätzliche Sicherungspfade`,"serverForm.backupPathsHint":`Einer pro Zeile, Platzhalter erlaubt; Daten-Envs werden bereits erfasst.`,"serverForm.backupIgnore":`Bekannte generierte Dateien beim Sichern ignorieren`,"serverForm.backupIgnoreHint":`Überspringt node_modules, dist, .next und die übrigen Caches, aus denen nichts wiederhergestellt wird.`,"serverForm.bootstrap":`Bootstrap`,"serverForm.bootstrapDescription":`Ein einmaliger Befehl vor dem ersten Start — Installationen, Migrationen, Downloads.`,"serverForm.runBootstrap":`Bootstrap ausführen`,"serverForm.bootstrapHint":`Aus bedeutet, der Eintrag hat keinen, und der Schlüssel wird als null gesendet.`,"serverForm.timeoutMs":`Timeout (ms)`,"serverForm.runOnce":`Nur einmal pro Sitzung`,"serverForm.runOnceHint":`Aus bedeutet, dass er bei jedem Start erneut läuft.`,"serverForm.bootstrapArgs":`Bootstrap-Argumente`,"serverForm.bootstrapArgsHint":`Eines pro Zeile; dieselben Platzhalter wie die Hauptargumente.`,"serverForm.bootstrapEnv":`Bootstrap-Umgebung`,"serverForm.bootstrapEnvHint":`KEY=value pro Zeile, nur an den Bootstrap-Prozess exportiert.`,"serverForm.onPortConflict":`Bei Portkonflikt`,"serverForm.logBufferLines":`Protokollpuffer-Zeilen`,"serverForm.maxRssMb":`Max. RSS (MB)`,"serverForm.maxRssHint":`Neustart oberhalb davon; leer oder 0 deaktiviert es.`,"serverForm.enabledHint":`Aus behält den Eintrag in der Konfiguration, startet ihn aber nicht.`,"settings.auth.policyTitle":`Zugriffsrichtlinie`,"settings.auth.policyDescription":`Die Authentifizierung gilt unabhängig davon, ob das Panel auf Loopback läuft oder im Netzwerk exponiert ist.`,"settings.auth.requirePassword":`Passwort verlangen`,"settings.auth.requirePasswordHint":`Jede Anfrage braucht eine angemeldete Sitzung.`,"settings.auth.sessionLifetime":`Sitzungsdauer (ms)`,"settings.auth.sessionLifetimeHint":`Leerlauf-Timeout: 604800000 ist eine Woche ohne Aktivität. Jede Anfrage startet sie neu.`,"settings.auth.cookieSecure":`Cookie Secure`,"settings.auth.cookieSecureAuto":`Auto — nur über HTTPS`,"settings.auth.cookieSecureAlways":`Immer`,"settings.auth.cookieSecureNever":`Nie — reines HTTP`,"settings.auth.trustProxy":`Hinter einem Reverse-Proxy`,"settings.auth.trustProxyOff":`Nein — x-forwarded-* ignorieren`,"settings.auth.trustProxyLoopback":`Ja, nur von dieser Maschine`,"settings.auth.trustProxyAny":`Ja, von jedem Aufrufer`,"settings.auth.trustProxyHint":`x-forwarded-* für Client-Adresse und Protokoll vertrauen. „Nur von dieser Maschine“ ist die sichere Wahl für einen Proxy auf demselben Host. „Von jedem Aufrufer“ glaubt dem eigenen Header des Aufrufers, daher zählt die Login-Sperre pro gefälschter Adresse und lässt sich umgehen.`,"settings.auth.maxLoginAttempts":`Max. Anmeldeversuche`,"settings.auth.maxLoginAttemptsHint":`Fehlversuche vor einer vorübergehenden Sperre.`,"settings.auth.lockout":`Sperre (ms)`,"settings.auth.apiTokenSet":`Ein API-Token ist gesetzt`,"settings.auth.apiTokenMissing":`Kein API-Token`,"settings.auth.apiTokenNote":`Skripte können die API mit {code} aufrufen, statt sich anzumelden.`,"settings.auth.noTokenNote":`Für Shell-Skripte und Agenten erzeugen Sie eines mit {command}.`,"settings.auth.defaultPasswordTitle":`Das Standardpasswort ist noch in Verwendung`,"settings.auth.defaultPasswordText":`Setzen Sie unten ein eigenes Passwort. Ein Lauschen jenseits von {address} wird bis dahin abgelehnt.`,"settings.auth.blockedTitle":`Dieser Listener wird abgelehnt`,"settings.auth.noPasswordTitle":`Authentifizierung ist an, aber kein Passwort gesetzt`,"settings.auth.noPasswordText":`Es wird noch nichts verlangt, und ein Lauschen jenseits von {address} bleibt abgelehnt, bis ein Passwort existiert.`,"settings.auth.exposedTitle":`Über 127.0.0.1 hinaus erreichbar`,"settings.auth.exposedText":`Halten Sie das Passwort stark, oder setzen Sie einen TLS-terminierenden Proxy vor das Panel.`,"settings.auth.changePassword":`Passwort ändern`,"settings.auth.setPassword":`Passwort setzen`,"settings.auth.passwordDescription":`Wird als scrypt-Hash in der Geheimnisdatei gespeichert, nie in der Konfiguration.`,"settings.auth.currentPassword":`Aktuelles Passwort`,"settings.auth.newPassword":`Neues Passwort`,"settings.auth.repeatPassword":`Neues Passwort wiederholen`,"settings.auth.passwordMismatch":`Die beiden Passwörter stimmen nicht überein.`,"settings.auth.updatePassword":`Passwort aktualisieren`,"settings.auth.setPasswordAndEnable":`Passwort setzen und Authentifizierung aktivieren`,"settings.auth.clearPassword":`Passwort löschen`,"settings.auth.clearPasswordConfirm":`Löschen bestätigen`,"settings.auth.clearPasswordTitle":`Das Passwort löschen?`,"settings.auth.clearPasswordHint":`Damit wird die Authentifizierung abgeschaltet, und jeder, der das Panel erreicht, kommt direkt hinein.`,"settings.auth.shortPasswordTitle":`Das ist ein kurzes Passwort`,"settings.auth.shortPasswordText":`Es ist erlaubt, aber Länge ist das Einzige, was zwischen dem Netzwerk und diesem Panel steht.`,"settings.auth.passwordUpdated":`Passwort aktualisiert. Andere Sitzungen wurden abgemeldet.`,"settings.auth.passwordCleared":`Passwort gelöscht und Authentifizierung deaktiviert.`,"settings.backup.createTitle":`Sicherung erstellen`,"settings.backup.createDescription":`Wählen Sie, was dieses Archiv erfasst. Nichts wird geschrieben, bis Sie es erstellen.`,"settings.backup.chooseItems":`Elemente wählen`,"settings.backup.partialCapture":`Nur die gewählten Elemente werden erfasst.`,"settings.backup.withoutGlobalSettings":`Ohne die globalen Einstellungen kann eine Wiederherstellung einen Arbeitsbereich nur dort platzieren, wo das Zielpanel ihn bereits kennt.`,"settings.backup.password":`Passwort (optional)`,"settings.backup.passwordHint":`Verschlüsselt das Archiv; beim Wiederherstellen wird dasselbe Passwort verlangt.`,"settings.backup.create":`Sicherung erstellen`,"settings.backup.chooseItemsFor":`{label} — Elemente wählen`,"settings.backup.subDescription":`Ein Arbeitsbereich kann ganz erfasst werden oder nur mit seinen Einstellungen, Servern, Geheimnissen und deklarierten Datenpfaden.`,"settings.backup.selectedOf":`{selected} von {total} ausgewählt`,"settings.backup.nothingSelectable":`Dieser Arbeitsbereich deklariert noch nichts Auswählbares.`,"settings.backup.apply":`Übernehmen`,"settings.backup.created":`Sicherung erstellt.`,"settings.backup.leafSettingsHint":`Server-Standardwerte, Protokollaufbewahrung und Benachrichtigungen`,"settings.backup.leafServersHint":`Jeder Eintrag in servers.config.json`,"settings.backup.leafSecretsHint":`Telegram-Bot-Token und DDNS-Zugangsdaten`,"settings.backup.leafDataHint":`Ein deklarierter Datenpfad`,"settings.backup.pathFromOrigin":`{path} — von {origin}`,"settings.backup.entrySettingsHint":`Listener, Authentifizierung, Host-Werte und die Sicherungsrichtlinie`,"settings.backup.entrySecretsHint":`Passwort-Hash und API-Token-Hash`,"settings.backup.entryTlsHint":`Das eigene Zertifikatspaar des Panels`,"settings.backup.entryWorkspaceHint":`Seine Einstellungen, Server, Geheimnisse und deklarierten Datenpfade`,"settings.backup.entryGlobalSettings":`Globale Einstellungen`,"settings.backup.entryGlobalSecrets":`Globale Geheimnisse (Passwort, API-Token)`,"settings.backup.entryTlsCertificate":`TLS-Zertifikat`,"settings.backup.leafSettings":`Einstellungen`,"settings.backup.leafServers":`Server`,"settings.backup.leafSecrets":`Geheimnisse`,"settings.backup.secretPassword":`Passwort`,"settings.backup.secretPasswordHint":`Der Passwort-Hash des Panels selbst; seine Wiederherstellung setzt das alte Passwort zurück`,"settings.backup.secretApiToken":`API-Token`,"settings.backup.secretApiTokenHint":`Das langlebige Token, das Skripte und Agenten verwenden`,"settings.backup.secretTelegram":`Telegram-Bot-Token`,"settings.backup.secretTelegramHint":`Wird für Benachrichtigungen verwendet`,"settings.backup.secretDdns":`DDNS-Zugangsdaten`,"settings.backup.secretDdnsHint":`Zugangsdaten des Anbieters, versiegelt mit ihrem Schlüssel aufbewahrt`,"settings.backup.restoreItemLabel":`{workspace}: {item}`,"settings.backup.noteMissing":`existiert noch nicht`,"settings.backup.noteCovered":`abgedeckt durch {path}`,"settings.backup.noteAlreadyDeclared":`bereits von {origin} deklariert`,"settings.backup.noteContainsBackupDir":`enthält das Sicherungsverzeichnis`,"settings.backup.noteUnknownWorkspace":`unbekannter Arbeitsbereich`,"settings.backup.noteMissingFromArchive":`fehlt im Archiv`,"settings.backup.noteDeclaredByBackup":`von der Konfiguration der Sicherung deklariert, die nicht wiederhergestellt wird`,"settings.backup.noteNotDeclared":`weder von dieser Konfiguration noch von der Sicherung deklariert`,"settings.backup.noteRestoredFrom":`wiederhergestellt aus {path}`,"settings.backups.policyTitle":`Richtlinie`,"settings.backups.policyDescription":`Wie viele Archive das Panel behält und welche zusätzlichen Pfade jedes davon erfasst.`,"settings.backups.allow":`Sicherungen erlauben`,"settings.backups.allowHint":`Aus stoppt neue Archive; was bereits auf der Platte liegt, bleibt herunterladbar und wiederherstellbar.`,"settings.backups.offTitle":`Sicherungen sind aus`,"settings.backups.offText":`Nichts Neues wird archiviert, bis dies wieder an ist. Die Archive unten können weiterhin heruntergeladen, wiederhergestellt und gelöscht werden.`,"settings.backups.keep":`Behalten`,"settings.backups.keepHint":`Ältere Archive werden nach jeder Sicherung entfernt.`,"settings.backups.schedule":`Automatische Sicherungen`,"settings.backups.scheduleHint":`Das Panel erstellt in diesem Abstand selbst ein Archiv.`,"settings.backups.scheduleHours":`Alle (Stunden)`,"settings.backups.scheduleHoursHint":`24 ist ein Archiv pro Tag.`,"settings.backups.scheduleKeep":`Automatische behalten`,"settings.backups.scheduleKeepHint":`Manuelle Archive zählen separat, über Behalten.`,"settings.backups.scheduleLast":`Letzte automatische: {when}`,"settings.backups.extraPaths":`Zusätzliche Pfade`,"settings.backups.extraPathsHint":`Kommagetrennt, in jedem Archiv zusätzlich zu dem erfasst, was jeder Arbeitsbereich deklariert.`,"settings.backups.extraPathsPlaceholder":`{home}/myapp-data`,"settings.backups.archiveDirectory":`Archivverzeichnis`,"settings.backups.copyArchiveDirectory":`Das Archivverzeichnis kopieren`,"settings.backups.changeDirectory":`Eine Änderung ist eine Entscheidung auf Dateiebene: bearbeiten Sie {path}.`,"settings.backups.theSettingsFile":`die Einstellungsdatei`,"settings.backups.capturesTitle":`Was eine Sicherung erfasst`,"settings.backups.capturesDescription":`Wählen Sie diese im Erstellen-Dialog. Ein Arbeitsbereich kann ganz genommen werden oder nur mit seinen Einstellungen, Servern, Geheimnissen und deklarierten Datenpfaden.`,"settings.backups.nothingSelectable":`Noch ist nichts auswählbar.`,"settings.backups.fromOrigin":`von {origin}`,"settings.backups.notCaptured":`nicht erfasst`,"settings.backups.generatedSkipped":`generierte Verzeichnisse übersprungen`,"settings.backups.included":`enthalten`,"settings.backups.skipped":`übersprungen`,"settings.backups.workspacesNote":`Dieses Panel bedient {count} Arbeitsbereiche; jeder ist ein eigener auswählbarer Eintrag.`,"settings.backups.archivesTitle":`Archive`,"settings.backups.archivesDescription":`Im Sicherungsverzeichnis gespeichert und jederzeit herunterladbar.`,"settings.backups.create":`Sicherung erstellen…`,"settings.backups.restore":`Wiederherstellen…`,"settings.backups.noArchives":`Noch keine Archive`,"settings.backups.noArchivesEnabled":`Erstellen Sie jetzt eine, um das Panel und die gewählten Arbeitsbereiche zu erfassen.`,"settings.backups.noArchivesDisabled":`Es wurde noch nichts archiviert, und Sicherungen sind aus.`,"settings.backups.encrypted":`verschlüsselt`,"settings.backups.download":`Herunterladen`,"settings.backups.restoreAction":`Wiederherstellen`,"settings.backups.confirmDelete":`Löschen bestätigen`,"settings.backups.deleted":`{name} gelöscht.`,"settings.backups.kindSettings":`Einstellungen`,"settings.backups.kindServers":`Server`,"settings.backups.kindSecrets":`Geheimnisse`,"settings.backups.kindTls":`tls`,"settings.backups.kindData":`Daten`,"settings.backups.kindWorkspace":`Arbeitsbereich`,"settings.ddns.statePending":`Wartet`,"settings.ddns.stateOk":`Aktualisiert`,"settings.ddns.stateUnchanged":`Aktuell`,"settings.ddns.stateSkipped":`Übersprungen`,"settings.ddns.stateError":`Fehlgeschlagen`,"settings.ddns.passFinished":`Dynamic-DNS-Durchlauf beendet`,"settings.ddns.credentialsSaved":`Zugangsdaten für {account} gespeichert`,"settings.ddns.credentialsRemoved":`Zugangsdaten für {account} entfernt`,"settings.ddns.loadErrorTitle":`Dynamic-DNS-Einstellungen konnten nicht geladen werden`,"settings.ddns.title":`Dynamic DNS`,"settings.ddns.description":`Halten Sie diese Hostnamen auf die öffentliche Adresse dieser Maschine gerichtet. Zugangsdaten liegen in der Geheimnisdatei, nie in servers.config.json.`,"settings.ddns.checking":`wird geprüft`,"settings.ddns.needsAttention":`braucht Aufmerksamkeit`,"settings.ddns.watching":`überwacht`,"settings.ddns.updateNow":`Jetzt aktualisieren`,"settings.ddns.enabled":`Dynamic DNS aktuell halten`,"settings.ddns.enabledHint":`Ein Durchlauf erfolgt im untenstehenden Intervall; ein Anbieter wird nur aufgerufen, wenn sich die Adresse tatsächlich geändert hat.`,"settings.ddns.interval":`Prüfen alle (ms)`,"settings.ddns.intervalHint":`60000 ist eine Minute; der Standardwert sind fünf.`,"settings.ddns.ttl":`TTL (s)`,"settings.ddns.ttlHint":`1 bedeutet automatisch, wo der Anbieter das unterstützt.`,"settings.ddns.detectIpv4":`IPv4 ermitteln`,"settings.ddns.detectIpv4Hint":`Nötig für A-Einträge.`,"settings.ddns.detectIpv6":`IPv6 ermitteln`,"settings.ddns.detectIpv6Hint":`Nötig für AAAA-Einträge.`,"settings.ddns.accountsTitle":`Anbieterkonten`,"settings.ddns.accountsDescription":`Ein Eintrag pro API-Token. Zugangsdaten werden auf dem Server gespeichert und kommen nie zurück.`,"settings.ddns.credentialsSet":`Zugangsdaten gespeichert`,"settings.ddns.noCredentials":`keine Zugangsdaten`,"settings.ddns.credentials":`Zugangsdaten`,"settings.ddns.removeAccountBlocked":`Entfernen Sie zuerst die Hostnamen, die dieses Konto verwenden`,"settings.ddns.noAccounts":`Noch keine Konten. Wählen Sie einen Anbieter und fügen Sie eines hinzu.`,"settings.ddns.provider":`Anbieter`,"settings.ddns.accountId":`Konto-Id`,"settings.ddns.accountIdPlaceholder":`cloudflare-home`,"settings.ddns.accountIdHint":"Die Konto-Id benennt das Konto und verschlüsselt seine gespeicherten Zugangsdaten; Kleinbuchstaben, Ziffern, `-` und `_`.","settings.ddns.hostnamesTitle":`Hostnamen`,"settings.ddns.hostnamesDescription":`Die ganze Liste, die aktuell gehalten wird — suba.domain.com, domain.xyz und so weiter.`,"settings.ddns.account":`Konto`,"settings.ddns.recordA":`A (IPv4)`,"settings.ddns.recordAaaa":`AAAA (IPv6)`,"settings.ddns.aaaaUnsupported":`Dieser Anbieter kann keine AAAA-Einträge verwalten.`,"settings.ddns.proxied":`Proxied`,"settings.ddns.proxiedHint":`Cloudflare liefert diesen Eintrag über sein eigenes Netzwerk aus.`,"settings.ddns.ipv6Off":`IPv6-Erkennung ist aus`,"settings.ddns.advanced":`Erweitert`,"settings.ddns.advancedHint":`registrierte Domain und TTL-Überschreibung`,"settings.ddns.registeredDomain":`Registrierte Domain`,"settings.ddns.registeredDomainPlaceholder":`example.co.uk`,"settings.ddns.registeredDomainHint":`Nur wenn dem Anbieter die Apex mitgeteilt werden muss, z. B. bei Domains mit drei Labels.`,"settings.ddns.ttlOverride":`TTL-Überschreibung (s)`,"settings.ddns.ttlOverrideHint":`Leer verwendet den Wert oben.`,"settings.ddns.noHostnames":`Noch keine Hostnamen.`,"settings.ddns.hostnamePlaceholder":`home.example.com`,"settings.ddns.lastPass":`Letzter Durchlauf`,"settings.ddns.ipv4Value":`IPv4 {address}`,"settings.ddns.ipv6Value":`IPv6 {address}`,"settings.ddns.type":`Typ`,"settings.ddns.state":`Zustand`,"settings.ddns.address":`Adresse`,"settings.ddns.detail":`Detail`,"settings.ddns.credentialsTitle":`Anbieter-Zugangsdaten`,"settings.ddns.credentialsDescription":`Gespeicherte Felder werden nie zurückgesendet. Speichern ersetzt jedes Feld für dieses Konto und führt sofort einen Durchlauf aus.`,"settings.ddns.optional":`Optional.`,"settings.ddns.whereToGet":`Woher Sie diese bekommen`,"settings.ddns.forget":`Verwerfen`,"settings.ddns.forgetConfirm":`Verwerfen`,"settings.ddns.forgetTitle":`Die gespeicherten Zugangsdaten verwerfen?`,"settings.ddns.forgetHint":`Sie werden von der Platte entfernt und lassen sich nicht zurücklesen; Sie müssten sie erneut einfügen.`,"settings.ddns.saveCredentials":`Zugangsdaten speichern`,"settings.defaults.title":`Server-Standardwerte`,"settings.defaults.description":`Gelten für jeden Eintrag, der keinen eigenen Wert setzt.`,"settings.defaults.enabledHint":`Neue Einträge starten aktiviert.`,"settings.defaults.autostart":`Autostart mit dem Panel`,"settings.defaults.bind":`Bindung`,"settings.defaults.bindLocal":`Lokal — 127.0.0.1`,"settings.defaults.bindLan":`LAN — 0.0.0.0`,"settings.defaults.onPortConflict":`Bei Portkonflikt`,"settings.defaults.conflictBlock":`Den Start blockieren`,"settings.defaults.conflictWarn":`Warnen und trotzdem starten`,"settings.defaults.conflictFollow":`Einem abgekoppelten Neustart seiner selbst folgen`,"settings.defaults.conflictReclaim":`Einen abgekoppelten Neustart durch eine überwachte Kopie ersetzen`,"settings.defaults.conflictKill":`Beenden, was den Port belegt`,"settings.defaults.logBufferLines":`Protokollpuffer-Zeilen`,"settings.defaults.logBufferLinesHint":`Pro Server im Speicher gehalten.`,"settings.destination.title":`Ziele`,"settings.destination.generalTitle":`Allgemein`,"settings.destination.generalDescription":`Was jedes Ziel befolgt, solange es keine eigenen Einstellungen hat.`,"settings.destination.description":`Wohin Ereignisse gehen. Jeder kann den allgemeinen Einstellungen oben folgen oder eigene haben.`,"settings.destination.addTelegram":`Telegram hinzufügen`,"settings.destination.addWebhook":`Webhook hinzufügen`,"settings.destination.empty":`Noch kein Ziel. Fügen Sie einen Telegram-Chat oder einen Webhook hinzu, um Benachrichtigungen zu erhalten.`,"settings.destination.chatId":`Chat-ID`,"settings.destination.chatIdHint":`Mit „Chats erkennen“ aus den Updates des Bots füllen.`,"settings.destination.botToken":`Bot-Token`,"settings.destination.saveToken":`Token speichern`,"settings.destination.tokenSet":`Token gesetzt`,"settings.destination.noToken":`kein Token`,"settings.destination.detectChats":`Chats erkennen`,"settings.destination.sendTest":`Test senden`,"settings.destination.remove":`Entfernen`,"settings.destination.removeQuestion":`{name} entfernen?`,"settings.destination.removeBody":`Ereignisse werden nicht mehr gesendet und ein gespeicherter Bot-Token wird entfernt.`,"settings.destination.ownSettings":`Eigene Einstellungen`,"settings.destination.ownSettingsHint":`Aus folgt den allgemeinen Einstellungen oben.`,"settings.destination.lastSend":`Letzter Versand: {result} {when}`,"settings.destination.testSent":`Test gesendet.`,"settings.host.title":`Schwellenwerte`,"settings.host.description":`Ein Schwellenwert von 0 deaktiviert diesen Alarm. Überschreitungen melden einmal und bei Erholung noch einmal.`,"settings.host.enabled":`Diese Maschine erfassen`,"settings.host.enabledHint":`Last, Speicher, Swap, Speicherplatz und CPU-Temperatur, im untenstehenden Intervall.`,"settings.host.interval":`Intervall (ms)`,"settings.host.intervalHint":`Wie oft der Host erfasst und Alarme geprüft werden.`,"settings.host.diskPaths":`Speicherplatz-Pfade`,"settings.host.diskPathsHint":`Kommagetrennt; Vorlagen wie {home} werden erweitert.`,"settings.host.diskUsed":`Speicherplatz belegt (%)`,"settings.host.memoryUsed":`Speicher belegt (%)`,"settings.host.swapUsed":`Swap belegt (%)`,"settings.host.zeroDisables":`0 deaktiviert den Alarm.`,"settings.host.loadPerCpu":`Last pro CPU`,"settings.host.loadPerCpuHint":`1-Minuten-Lastdurchschnitt geteilt durch die Kernzahl.`,"settings.host.tempCelsius":`CPU-Temperatur (°C)`,"settings.host.tempCelsiusHint":`Nur Linux; wird ignoriert, wo das Betriebssystem sie nicht bereitstellt.`,"settings.host.breachingNow":`Aktuell überschritten:`,"settings.host.nothingBreaching":`Derzeit wird nichts überschritten.`,"settings.interface.title":`Oberfläche`,"settings.interface.description":`Das Panel ist eine statische Seite: Behalten Sie die mitgelieferte, oder liefern Sie Ihren eigenen Build aus dem Statusverzeichnis aus.`,"settings.interface.currentlyServing":`Wird derzeit ausgeliefert`,"settings.interface.copyDirectory":`Das UI-Verzeichnis kopieren`,"settings.interface.fileCount":`{count} Dateien`,"settings.interface.sourceStock":`mitgeliefert`,"settings.interface.sourceCustom":`eigene`,"settings.interface.unnamed":`unbenannt`,"settings.interface.panelVersion":`Panel-Version`,"settings.interface.copyPanelVersion":`Die Panel-Version kopieren`,"settings.interface.panelVersionHint":`Die Version, die dieser Panel-Prozess ausführt — {command} gibt dieselbe Nummer aus.`,"settings.interface.panelName":`Panel-Name`,"settings.interface.panelNameHint":`Wird von der mitgelieferten UI in der Seitenleiste angezeigt. Eine eigene UI kann stattdessen ihren eigenen Namen zeigen.`,"settings.interface.uploadLabel":`Einen UI-Build hochladen (.zip mit einer index.html)`,"settings.interface.docsHint":`Siehe {docs} für den API-Vertrag. Wenn eine eigene UI das Panel lahmlegt, setzt {command} vom Terminal aus die mitgelieferte wieder ein.`,"settings.interface.resetName":`Namen zurücksetzen`,"settings.interface.confirmRevert":`Zurücksetzen bestätigen`,"settings.interface.revertToStock":`Auf mitgelieferte zurücksetzen`,"settings.interface.install":`UI installieren`,"settings.interface.installed":`UI durch {name} ersetzt — laden Sie die Seite neu`,"settings.interface.yourBuild":`Ihr Build`,"settings.interface.restored":`mitgelieferte UI wiederhergestellt — laden Sie die Seite neu`,"settings.interface.nothingInstalled":`es war keine eigene UI installiert`,"settings.listener.title":`Listener`,"settings.listener.description":`Wo das Panel antwortet. Eine Änderung startet das Panel neu und schickt diese Seite an die neue Adresse.`,"settings.listener.port":`Steuerport`,"settings.listener.portHint":`Standard 3999.`,"settings.listener.bind":`Bindung`,"settings.listener.bindLocal":`Lokal`,"settings.listener.bindLan":`LAN`,"settings.listener.bindCustom":`Benutzerdefiniert`,"settings.listener.bindLanHint":`0.0.0.0 — aus dem ganzen Netzwerk erreichbar.`,"settings.listener.bindLocalHint":`127.0.0.1 — nur diese Maschine.`,"settings.listener.bindCustomHint":`Eine bestimmte Schnittstellenadresse, die Sie unten eingeben.`,"settings.listener.ipv4":`IPv4-Adresse`,"settings.listener.ipv4Placeholder":`192.168.1.20`,"settings.listener.ipv4Error":`Geben Sie eine IPv4-Adresse ein, zum Beispiel 192.168.1.20.`,"settings.listener.ipv4Hint":`Ein Lauschen jenseits von 127.0.0.1 braucht ein echtes Passwort.`,"settings.listener.openBrowser":`Einen Browser öffnen, wenn das Panel hochkommt`,"settings.listener.liveAddress":`Live-Adresse`,"settings.listener.restartRequired":`Neustart erforderlich`,"settings.listener.copyLiveAddress":`Die Live-Adresse kopieren`,"settings.listener.liveAddressHint":`Woran der Listener gerade gebunden ist; gespeicherte Änderungen wirken, sobald das Panel neu bindet.`,"settings.logs.title":`Aufbewahrung`,"settings.logs.description":`Die Live-Ansicht kommt immer aus dem Speicher; diese Dateien überleben einen Neustart und speisen die Protokollseite.`,"settings.logs.persist":`Protokolle auf der Platte behalten`,"settings.logs.persistHint":`Ausschalten, um alles nur im Speicher zu halten.`,"settings.logs.maxBytes":`Rotieren bei (Bytes)`,"settings.logs.maxBytesHint":`Pro Server, bevor auf .1, .2, … gerollt wird`,"settings.logs.keep":`Rotierte Dateien behalten`,"settings.logs.directory":`Protokollverzeichnis`,"settings.logs.copyDirectory":`Das Protokollverzeichnis kopieren`,"settings.logs.browseHint":`Diese Dateien auf der Seite {logs} durchsuchen und herunterladen.`,"settings.paths.dataRoot":`Datenwurzel`,"settings.paths.dataRootHint":`HHOSTED_HOME — globale Einstellungen, Arbeitsbereiche, TLS, Sicherungen und die UI.`,"settings.paths.globalSettings":`Globale Einstellungen`,"settings.paths.globalSettingsHint":`settings.json — Listener, Authentifizierung, TLS-Richtlinie, Host-Werte, Sicherungen.`,"settings.paths.projectDir":`Projektverzeichnis`,"settings.paths.projectDirHint":`Basis für relative Eintragspfade.`,"settings.paths.panelAddress":`Panel-Adresse`,"settings.paths.panelAddressHint":`Wo dieses Kontrollpanel gerade live ist.`,"settings.paths.serversConfig":`Server-Konfiguration`,"settings.paths.serversConfigHint":`servers.config.json von {label}.`,"settings.paths.workspaceSettings":`Arbeitsbereich-Einstellungen`,"settings.paths.workspaceSettingsHint":`Server-Standardwerte, Protokollaufbewahrung, Benachrichtigungen, DDNS.`,"settings.paths.logs":`Protokolle`,"settings.paths.logsHint":`Gespeicherte Ausgabe für diesen Arbeitsbereich.`,"settings.paths.copyPath":`{label} kopieren`,"settings.paths.whereThingsLive":`Wo was liegt`,"settings.paths.whereThingsLiveDescription":`Schreibgeschützt. Kopieren Sie einen Pfad direkt in eine Shell oder einen Editor.`,"settings.paths.workspaceFiles":`Arbeitsbereich-Dateien`,"settings.paths.workspaceFilesDescription":`Jeder Arbeitsbereich besitzt seine Konfiguration, Einstellungen und sein Protokollverzeichnis; nichts wird zwischen ihnen geteilt.`,"settings.rebind.failed":`Das Kontrollpanel hat auf {target} nicht geantwortet. Prüfen Sie die Konsole auf den neuen Listener.`,"settings.restore.title":`Eine Sicherung wiederherstellen`,"settings.restore.description":`Nichts wird geschrieben, bis der Plan angewendet wird.`,"settings.restore.archive":`Archiv`,"settings.restore.archiveHint":`Ein gespeichertes Archiv oder eine .zip von dieser Maschine.`,"settings.restore.encryptedName":`{name} — verschlüsselt`,"settings.restore.uploadOption":`Eine Datei hochladen…`,"settings.restore.archiveFile":`Archivdatei (.zip)`,"settings.restore.unchangedTitle":`Es hat sich noch nichts geändert`,"settings.restore.archiveLine":`Archiv {name}.`,"settings.restore.uploadedArchive":`Das hochgeladene Archiv.`,"settings.restore.passwordProtected":`Diese Sicherung ist passwortgeschützt.`,"settings.restore.password":`Passwort`,"settings.restore.checkAgain":`Mit dem Passwort erneut prüfen`,"settings.restore.choosePrompt":`Wählen Sie, was wiederhergestellt wird — {selected} von {total} ausgewählt.`,"settings.restore.willRestore":`Wird wiederhergestellt: {items}.`,"settings.restore.skipped":`Übersprungen: {items}.`,"settings.restore.nothing":`nichts`,"settings.restore.restartNote":`Die eigenen Einstellungen des Panels weichen ab, starten Sie das Panel danach neu.`,"settings.restore.reloadedNote":`Wiederhergestellte Arbeitsbereiche werden sofort neu geladen; als Autostart markierte Einträge starten von selbst.`,"settings.restore.applyStored":`Diese Wiederherstellung anwenden`,"settings.restore.applyUpload":`Den Upload wiederherstellen`,"settings.restore.restored":`{count} Element(e) wiederhergestellt.`,"settings.restore.restoredWithNotes":`{count} Element(e) wiederhergestellt — {notes}.`,"settings.restore.noteReloaded":`die wiederhergestellten Server sind live, Autostart-Einträge starten`,"settings.restore.noteRestart":`das Panel neu starten, um seine eigenen Einstellungen anzuwenden`,"settings.restore.reasonSymbolicLink":`symbolischer Link, übersprungen`,"settings.restore.reasonNotSelected":`nicht ausgewählt`,"settings.restore.reasonUnrestorable":`kann hier nicht wiederhergestellt werden`,"settings.restore.reasonInvalidSettings":`die Einstellungen im Archiv sind ungültig`,"settings.restore.reasonInvalidServers":`die Server im Archiv sind ungültig`,"settings.shell.noUnsavedChanges":`Keine ungespeicherten Änderungen`,"settings.shell.discard":`Änderungen verwerfen`,"settings.shell.save":`Einstellungen speichern`,"settings.shell.unsavedTitle":`Ungespeicherte Änderungen`,"settings.shell.nothingToSave":`Nichts zu speichern.`,"settings.telegram.title":`Telegram`,"settings.telegram.description":`Die Richtlinie liegt in der Konfiguration; das Bot-Token ist ein Geheimnis und kommt nie vom Server zurück.`,"settings.telegram.enabled":`Benachrichtigungen senden`,"settings.telegram.enabledHint":`Eine Nachricht pro Ereignis, gedrosselt durch die untenstehende Abklingzeit.`,"settings.telegram.chatId":`Chat-Id`,"settings.telegram.chatIdPlaceholder":`123456789 oder @channel`,"settings.telegram.chatIdHint":`Nutzen Sie Chats erkennen, um dies aus den Updates des Bots zu füllen.`,"settings.telegram.cooldown":`Abklingzeit pro Ereignis (ms)`,"settings.telegram.cooldownHint":`Pro Server und Grund, damit ein flatternder Server nicht spammen kann.`,"settings.telegram.onCrash":`Wiederholungen erschöpft`,"settings.telegram.onUnhealthy":`Port ungesund`,"settings.telegram.onForcedRestart":`Erzwungener Neustart`,"settings.telegram.onRecovered":`Wiederhergestellt`,"settings.telegram.onHost":`Host-Werte überschritten`,"settings.telegram.onHostHint":`Speicherplatz, Speicher, Swap, Last und CPU-Temperatur, gemäß den Schwellenwerten unter Host-Werte.`,"settings.telegram.onDdns":`Dynamic DNS geändert oder fehlgeschlagen`,"settings.telegram.onDdnsHint":`Eine Nachricht pro Durchlauf, der einen Eintrag geändert hat, und eine für einen fehlgeschlagenen Durchlauf.`,"settings.telegram.botToken":`Bot-Token`,"settings.telegram.tokenSet":`Token gesetzt`,"settings.telegram.noToken":`kein Token`,"settings.telegram.tokenNote":`Von @BotFather — ein Geheimnis, nie in servers.config.json.`,"settings.telegram.tokenPlaceholder":`123456789:AA…`,"settings.telegram.tokenSavedPlaceholder":`Gespeichert — geben Sie ein neues Token ein, um es zu ersetzen`,"settings.telegram.saveToken":`Token speichern`,"settings.telegram.detectChats":`Chats erkennen`,"settings.telegram.sendTest":`Test senden`,"settings.telegram.removeToken":`Token entfernen`,"settings.telegram.removeTokenConfirm":`Entfernen bestätigen`,"settings.telegram.removeTokenTitle":`Das Bot-Token entfernen?`,"settings.telegram.removeTokenHint":`Benachrichtigungen hören auf, bis Sie wieder ein Token einfügen.`,"settings.telegram.tokenSaved":`Token gespeichert.`,"settings.telegram.tokenSavedFor":`Token für @{username} gespeichert.`,"settings.telegram.tokenRemoved":`Token entfernt.`,"settings.telegram.noChats":`Keine Chats gefunden. Senden Sie zuerst /start an den Bot und erkennen Sie dann erneut.`,"settings.telegram.testSent":`Testnachricht gesendet.`,"settings.telegram.testFailed":`Die Testnachricht ist fehlgeschlagen.`,"settings.telegram.chatsSeen":`Chats, die der Bot gesehen hat:`,"settings.telegram.lastSend":`Letzter Versand: {result}`,"settings.tls.title":`HTTPS`,"settings.tls.description":`Laden Sie ein PEM-Paar hoch, und das Panel liefert TLS auf seinem eigenen Listener. Ein selbstsigniertes Zertifikat ist im Heimnetz in Ordnung.`,"settings.tls.serve":`Das Panel über HTTPS ausliefern`,"settings.tls.serveHint":`Wird mit Einstellungen speichern gespeichert; das Panel bindet neu und diese Seite folgt ihm.`,"settings.tls.certificateTitle":`Zertifikat`,"settings.tls.certificateDescription":`PEM-kodiertes Zertifikat und sein privater Schlüssel. Beide bleiben in der Geheimnisdatei.`,"settings.tls.unusableTitle":`Das gespeicherte Zertifikat ist nicht verwendbar`,"settings.tls.subject":`Subjekt`,"settings.tls.issuer":`Aussteller`,"settings.tls.validUntil":`Gültig bis`,"settings.tls.days":`{count} Tage`,"settings.tls.privateKey":`Privater Schlüssel`,"settings.tls.keyMatches":`passt zum Zertifikat`,"settings.tls.keyMismatch":`passt nicht`,"settings.tls.fingerprint":`Fingerabdruck`,"settings.tls.copyFingerprint":`Den Fingerabdruck kopieren`,"settings.tls.noCertificateTitle":`Kein Zertifikat hochgeladen`,"settings.tls.noCertificateText":`Laden Sie unten ein PEM-Zertifikat und einen Schlüssel hoch und speichern Sie dann. HTTPS wird erst ausgeliefert, wenn ein Zertifikat vorhanden ist.`,"settings.tls.certFile":`Zertifikatsdatei (.pem, .crt)`,"settings.tls.keyFile":`Datei mit privatem Schlüssel (.pem, .key)`,"settings.tls.pasteCertificate":`Oder das Zertifikat einfügen`,"settings.tls.pasteKey":`Oder den privaten Schlüssel einfügen`,"settings.tls.saveCertificate":`Zertifikat speichern`,"settings.tls.removeCertificate":`Zertifikat entfernen`,"settings.tls.confirmRemove":`Entfernen bestätigen`,"settings.tls.removeTitle":`Das Zertifikat entfernen?`,"settings.tls.removeHint":`TLS bleibt an, hat aber nichts auszuliefern, daher fällt das Panel auf http zurück, sobald der Listener neu bindet.`,"settings.tls.stored":`Zertifikat gespeichert.`,"settings.tls.removed":`Zertifikat entfernt.`,"settings.webhook.title":`Webhooks`,"settings.webhook.description":`Sendet dieselben Ereignisse an jeden Ort, der eine HTTP-Anfrage annimmt.`,"settings.webhook.empty":`Noch kein Webhook. Fügen Sie einen hinzu, um Ereignisse woandershin zu senden.`,"settings.webhook.add":`Webhook hinzufügen`,"settings.webhook.newTarget":`hook`,"settings.webhook.url":`URL`,"settings.webhook.urlPlaceholder":`https://ntfy.sh/my-topic`,"settings.webhook.method":`Methode`,"settings.webhook.format":`Body`,"settings.webhook.formatJson":`JSON`,"settings.webhook.formatText":`Reiner Text`,"settings.webhook.formatHint":`JSON für Discord, Slack oder den eigenen Endpunkt; reiner Text für ntfy.`,"settings.webhook.headers":`Header`,"settings.webhook.headersHint":`Ein Name: value pro Zeile, bei jeder Anfrage mitgesendet. ntfy liest hier Title und Priority; ein Bearer-Token gehört in Authorization.`,"settings.webhook.presets":`Vorlagen`,"settings.webhook.presetNtfy":`ntfy`,"settings.webhook.presetDiscord":`Discord`,"settings.webhook.presetSlack":`Slack`,"settings.webhook.sendTest":`Test senden`,"settings.webhook.testSent":`Test gesendet.`,"settings.webhook.cooldown":`Abklingzeit (ms)`,"settings.webhook.cooldownHint":`Pro Server und Grund, nur für dieses Ziel. 0 deaktiviert es.`,"settings.webhook.lastSend":`Letzter Versand: {result} {when}`,"settings.webhook.removeQuestion":`{name} entfernen?`,"settings.webhook.removeBody":`Ereignisse werden nicht mehr gesendet. URL und Header werden aus der Konfiguration dieses Arbeitsbereichs entfernt.`,"status.stopped":`Gestoppt`,"status.starting":`Startet`,"status.running":`Läuft`,"status.stopping":`Stoppt`,"status.backoff":`Neustart`,"status.crashed":`Abgestürzt`,"status.conflict":`Portkonflikt`,"theme.label":`Design: {theme}`,"theme.light":`hell`,"theme.dark":`dunkel`,"theme.system":`System`,"topbar.search":`Suchen oder Befehl ausführen`,"topbar.openNavigation":`Navigation öffnen`,"topbar.commandPalette":`Befehlspalette`,"topbar.account":`Konto`,"topbar.signedIn":`Angemeldet`,"topbar.signOut":`Abmelden`,"topbar.workspaceLabel":`Arbeitsbereich: {name}`,"validation.port":`Der Port muss eine ganze Zahl zwischen 1 und 65535 sein.`,"validation.idRequired":`Eine Id ist erforderlich — sie ist der Schlüssel, unter dem dieser Server läuft.`,"validation.idPattern":`Die Id muss mit einem Kleinbuchstaben oder einer Ziffer beginnen und darf nur Kleinbuchstaben, Ziffern, Bindestriche und Unterstriche enthalten.`,"validation.commandRequired":`Ein Befehl ist erforderlich — die ausführbare Datei, die der Supervisor startet.`,"validation.bootstrapCommand":`Ein Bootstrap braucht einen auszuführenden Befehl.`,"views.actions.addAServer":`Einen Server hinzufügen`,"views.actions.addServer":`Server hinzufügen`,"views.actions.startAll":`Alle starten`,"views.actions.stopAll":`Alle stoppen`,"views.common.thisWorkspace":`dieser Arbeitsbereich`,"views.globalOverview.configError":`Die Konfiguration konnte nicht gelesen werden: {error}`,"views.globalOverview.degraded":`beeinträchtigt`,"views.globalOverview.description":`Host-Werte und jeder Server aus jedem Arbeitsbereich, gruppiert nach seinem Ort.`,"views.globalOverview.emptyDescription":`Wählen Sie oben einen Arbeitsbereich und fügen Sie den ersten Prozess hinzu, den er am Leben halten soll.`,"views.globalOverview.emptyTitle":`Noch nirgends Server`,"views.globalOverview.groupEmptyDescription":`Fügen Sie einen hinzu, und er erscheint hier und auf der Arbeitsbereichsseite.`,"views.globalOverview.groupRunning":`{running}/{total} laufen`,"views.globalOverview.hostUptime":`Host-Laufzeit`,"views.globalOverview.hostVitals":`Host-Werte`,"views.globalOverview.hostVitalsNote":`Diese Maschine, von jedem Arbeitsbereich geteilt.`,"views.globalOverview.openWorkspace":`Arbeitsbereich öffnen`,"views.globalOverview.panelUptime":`Panel-Laufzeit`,"views.globalOverview.samplingOff":`Sampling aus`,"views.globalOverview.samplingOn":`Sampling an`,"views.globalOverview.serversTotal":`{count} Server insgesamt`,"views.globalOverview.serving":`wird ausgeliefert`,"views.globalOverview.workspaces":`Arbeitsbereiche`,"views.globalSettings.authenticationDescription":`Wer sich bei diesem Panel anmelden darf und wie einer Sitzung vertraut wird.`,"views.globalSettings.backupsDescription":`Ein Archiv pro Erfassung, das das Panel und jeden gewählten Arbeitsbereich abdeckt.`,"views.globalSettings.changesDescription":`Was Speichern in die Einstellungsdatei des Panels schreiben würde.`,"views.globalSettings.description":`Listener, Zugriff, Host-Werte, Sicherungen, TLS und Pfade für dieses Panel — von jedem Arbeitsbereich geteilt.`,"views.globalSettings.hostDescription":`Alarmschwellen für die eigenen Ressourcen dieser Maschine.`,"views.globalSettings.interfaceDescription":`Liefern Sie Ihre eigene Panel-UI statt der mitgelieferten aus.`,"views.globalSettings.languageDescription":`Die Sprache, in der dieses Panel angezeigt wird, für diesen Browser.`,"views.globalSettings.languageNote":`Ihre Wahl wird in diesem Browser gespeichert und gilt auch für die Anmeldeseite. Sie ändert weder die eigene Protokollausgabe des Panels noch seine API-Meldungen.`,"views.globalSettings.listenerDescription":`Wo das Kontrollpanel lauscht und wie es sich ankündigt.`,"views.globalSettings.loadErrorTitle":`Einige reine Dateiwerte konnten nicht geladen werden`,"views.globalSettings.navLabel":`Abschnitte der globalen Einstellungen`,"views.globalSettings.pathsDescription":`Wo home-hosted seine Dateien ablegt, global und pro Arbeitsbereich.`,"views.globalSettings.saveFailed":`Die Einstellungen konnten nicht gespeichert werden.`,"views.globalSettings.saved":`Einstellungen gespeichert.`,"views.globalSettings.savedRebinding":`Einstellungen gespeichert — das Panel wechselt zu einer neuen Adresse.`,"views.globalSettings.savedToast":`Einstellungen gespeichert`,"views.globalSettings.sectionAuthentication":`Authentifizierung`,"views.globalSettings.sectionBackups":`Sicherungen`,"views.globalSettings.sectionHost":`Host-Werte`,"views.globalSettings.sectionInterface":`Oberfläche`,"views.globalSettings.sectionListener":`Listener`,"views.globalSettings.sectionPaths":`Pfade`,"views.globalSettings.sectionTls":`TLS`,"views.globalSettings.title":`Globale Einstellungen`,"views.globalSettings.tlsDescription":`Liefern Sie das Panel über HTTPS mit Ihrem eigenen Zertifikat aus.`,"views.logs.allStreams":`Alle Streams`,"views.logs.clear":`Leeren`,"views.logs.clearDescription":`{server} — die Datei auf der Platte wird abgeschnitten. Der Prozess läuft weiter, und neue Zeilen werden ab hier geschrieben.`,"views.logs.clearFile":`Datei leeren`,"views.logs.clearNote":`Laden Sie sie zuerst herunter, falls die Ausgabe noch gebraucht wird — das lässt sich vom Panel aus nicht rückgängig machen.`,"views.logs.clearTitle":`Die gespeicherte Protokolldatei leeren?`,"views.logs.fileEmpty":`Die gespeicherte Datei ist leer.`,"views.logs.loadedLines":`{count} Zeilen geladen`,"views.logs.modeLive":`Live-Stream`,"views.logs.modePersisted":`Gespeichert`,"views.logs.noServersDescription":`Fügen Sie einen hinzu, und seine Ausgabe wird hier gespeichert.`,"views.logs.noServersTitle":`Keine Server`,"views.logs.nothingOnDisk":`Nichts auf der Platte für diesen Server — die Persistenz ist aus, oder er hat noch nichts geschrieben.`,"views.logs.nothingPersisted":`nichts gespeichert`,"views.logs.persistedSummary":`{workspace} · {persisted} von {total} Servern auf der Platte · {size}`,"views.logs.persistedTitle":`Gespeicherte Protokolle`,"views.logs.pickServerDescription":`Gespeicherte Ausgabe und Live-Streams gibt es pro Server. Wählen Sie einen aus der Liste.`,"views.logs.pickServerTitle":`Einen Server wählen`,"views.logs.reload":`Neu laden`,"views.logs.retentionNote":`Rotation und Aufbewahrung werden unter Arbeitsbereich-Einstellungen → Protokolle festgelegt.`,"views.logs.searchAria":`Das Protokoll durchsuchen`,"views.logs.searchPlaceholder":`das Tail-Fenster durchsuchen`,"views.logs.searchedLines":`{count} Zeilen durchsucht`,"views.logs.streamAria":`Stream`,"views.logs.tailAria":`Tail-Größe`,"views.logs.waitingOutput":`Verbunden — warte auf Ausgabe.`,"views.overview.acrossTree":`über den ganzen Baum`,"views.overview.cpuNow":`{value}% CPU jetzt`,"views.overview.crashedOrUnhealthy":`abgestürzt oder ungesund`,"views.overview.description":`Live-Zustand jedes Servers in diesem Arbeitsbereich, aktualisiert über einen einzigen Ereignisstrom.`,"views.overview.emptyDescription":`Fügen Sie den ersten Prozess hinzu, den dieser Arbeitsbereich am Leben halten soll.`,"views.overview.emptyTitle":`Keine Server in diesem Arbeitsbereich`,"views.overview.enabled":`{count} aktiviert`,"views.overview.longestUptime":`Längste Laufzeit`,"views.overview.needsAttention":`Braucht Aufmerksamkeit`,"views.overview.notUp":`{count} nicht gestartet`,"views.overview.nothingBroken":`nichts ist kaputt`,"views.overview.nothingRunning":`nichts läuft`,"views.overview.openFullList":`Die vollständige Liste öffnen`,"views.overview.peak":`Spitze {value}`,"views.overview.processMemory":`Prozessspeicher`,"views.overview.recentActivity":`Letzte Aktivität`,"views.overview.running":`Läuft`,"views.overview.sampling":`der Ressourcenverlauf wird alle 5 s erfasst und für diese Sitzung behalten`,"views.overview.stopped":`Gestoppt`,"views.overview.workspaceLabel":`Arbeitsbereich {id}`,"views.route.server":`Server`,"views.serverDetail.allServers":`Alle Server`,"views.serverDetail.autostart":`Autostart mit dem Panel`,"views.serverDetail.backToServers":`Zurück zu den Servern`,"views.serverDetail.bind":`Bindung`,"views.serverDetail.bindCustom":`{bind} — benutzerdefiniert`,"views.serverDetail.bindLan":`0.0.0.0 — jede Schnittstelle`,"views.serverDetail.bindLocal":`127.0.0.1 — nur diese Maschine`,"views.serverDetail.closeEditor":`Editor schließen`,"views.serverDetail.copyJson":`Die JSON-Konfiguration kopieren`,"views.serverDetail.crashes24":`Abstürze 24 h`,"views.serverDetail.dependencies":`Abhängigkeiten`,"views.serverDetail.detached":`abgekoppelt`,"views.serverDetail.detachedHint":`Dieser Prozess hat sich selbst ersetzt: Das Panel hat den Nachfolger übernommen, statt eine zweite Kopie zu starten. Stopp funktioniert weiterhin, seine Ausgabe bleibt dort, wohin der Nachfolger sie geschickt hat.`,"views.serverDetail.disableServer":`Server deaktivieren`,"views.serverDetail.editHint":`Jedes Feld dieses Eintrags bearbeiten`,"views.serverDetail.effectiveConfig":`Wirksame Konfiguration`,"views.serverDetail.effectiveConfigNote":`Standardwerte aus den Einstellungen sind bereits in diese Werte eingemischt.`,"views.serverDetail.enableServer":`Server aktivieren`,"views.serverDetail.eventHistory":`Ereignisverlauf`,"views.serverDetail.eventHistoryNote":`Vom Panel 24 Stunden lang behalten.`,"views.serverDetail.fieldArgs":`args`,"views.serverDetail.fieldBackupPaths":`Sicherungspfade`,"views.serverDetail.fieldBootstrap":`bootstrap`,"views.serverDetail.fieldCommand":`command`,"views.serverDetail.fieldCwd":`cwd`,"views.serverDetail.fieldDataEnvs":`Daten-Envs`,"views.serverDetail.fieldDependsOn":`hängt ab von`,"views.serverDetail.fieldEnv":`env`,"views.serverDetail.fieldEnvFile":`Env-Datei`,"views.serverDetail.fieldHealth":`Gesundheit`,"views.serverDetail.fieldLogBuffer":`Protokollpuffer`,"views.serverDetail.fieldOnPortConflict":`bei Portkonflikt`,"views.serverDetail.fieldPortBind":`Port · Bind`,"views.serverDetail.fieldRestart":`Neustart`,"views.serverDetail.fieldRssLimit":`RSS-Limit`,"views.serverDetail.fieldStop":`Stopp`,"views.serverDetail.fields":`Felder`,"views.serverDetail.goneDescription":`{id} ist nicht mehr in servers.config.json. Vielleicht wurde er in einem anderen Tab entfernt.`,"views.serverDetail.goneTitle":`Dieser Server ist weg`,"views.serverDetail.health":`Gesundheit`,"views.serverDetail.healthProbe":`Gesundheitsprüfung`,"views.serverDetail.lastCrash":`Letzter Absturz`,"views.serverDetail.lastProbe":`Letzte Prüfung`,"views.serverDetail.logBufferLines":`{count} Zeilen`,"views.serverDetail.memoryRss":`Speicher (RSS)`,"views.serverDetail.moreActions":`Weitere Aktionen für {label}`,"views.serverDetail.neverWritten":`Dieser Server hat noch nichts geschrieben.`,"views.serverDetail.nextAttempt":`Nächster Versuch`,"views.serverDetail.none":`keine`,"views.serverDetail.off":`aus`,"views.serverDetail.persistent":`persistent`,"views.serverDetail.persistentHint":"Dieser Eintrag läuft unter seiner eigenen Nanny und läuft deshalb weiter, wenn das Panel stoppt, neu startet oder beendet wird. `home-hosted down` meldet ihn, statt ihn zu stoppen; nur Stopp tut das.","views.serverDetail.pid":`PID`,"views.serverDetail.portFree":`frei`,"views.serverDetail.portInUse":`belegt`,"views.serverDetail.portState":`Portzustand`,"views.serverDetail.probeFailed":`Die Gesundheitsprüfung ist noch nicht durchgelaufen.`,"views.serverDetail.probeFailedFor":`Die Gesundheitsprüfung ist seit {duration} nicht durchgelaufen.`,"views.serverDetail.processes":`Prozesse`,"views.serverDetail.rawJson":`Rohes JSON`,"views.serverDetail.removeAutostart":`Aus dem Autostart entfernen`,"views.serverDetail.removeDescription":`{label} wird aus servers.config.json entfernt: sein Prozess wird gestoppt, und seine Protokolle und Absturzhistorie werden mit ihm gelöscht.`,"views.serverDetail.removeNote":`Das lässt sich vom Panel aus nicht rückgängig machen — der Eintrag muss von Hand oder aus einer Sicherung wieder hinzugefügt werden.`,"views.serverDetail.removeServer":`Server entfernen`,"views.serverDetail.removeTitle":`Diesen Server entfernen?`,"views.serverDetail.restart":`Neu starten`,"views.serverDetail.restarts":`Neustarts`,"views.serverDetail.sectionConfig":`Konfiguration`,"views.serverDetail.sectionHistory":`Verlauf`,"views.serverDetail.sectionOutput":`Live-Ausgabe`,"views.serverDetail.start":`Starten`,"views.serverDetail.startsOnItsOwn":`Startet von selbst. Fügen Sie Ids unter {code} hinzu, um ihn nach anderen Servern einzuordnen.`,"views.serverDetail.stop":`Stoppen`,"views.serverDetail.telemetry":`Telemetrie`,"views.serverDetail.telemetryNote":`Wird alle 5 s erfasst, solange diese Seite offen ist.`,"views.serverDetail.unhealthyFor":`Ungesund seit`,"views.serverDetail.uptime24":`24 h Laufzeit`,"views.serverDetail.waitingFirstLine":`Warte auf die erste Zeile…`,"views.servers.clearFilter":`Den Filter leeren`,"views.servers.clearFilters":`Filter leeren`,"views.servers.description":`Jeder Eintrag in servers.config.json von {workspace}, mit Live-Ressourcen, Gesundheit und Verlauf.`,"views.servers.emptyDescription":`Fügen Sie den ersten Prozess hinzu, den dieses Panel überwachen soll. Befehl, Argumente, Env und Arbeitsverzeichnis liegen alle in der Konfigurationsdatei.`,"views.servers.emptyTitle":`Keine Server konfiguriert`,"views.servers.filterAll":`Alle`,"views.servers.filterAria":`Server filtern`,"views.servers.filterAttention":`Aufmerksamkeit`,"views.servers.filterDisabled":`Deaktiviert`,"views.servers.filterPlaceholder":`Nach Id, Bezeichnung, Befehl oder Port filtern`,"views.servers.filterRunning":`Läuft`,"views.servers.filterStopped":`Gestoppt`,"views.servers.noMatchDescription":`Versuchen Sie einen anderen Status oder leeren Sie die Suche.`,"views.servers.noMatchTitle":`Nichts passt zu diesem Filter`,"views.settings.changesDescription":`Was Speichern in die Einstellungen von {workspace} schreiben würde.`,"views.settings.configErrorNote":`Das Panel überwacht weiter mit der bisherigen Konfiguration; eine Korrektur der Datei lädt sie von selbst neu.`,"views.settings.configErrorTitle":`Die Arbeitsbereich-Konfiguration konnte nicht gelesen werden`,"views.settings.configWarningTitle":`Die Konfiguration dieses Arbeitsbereichs hat eine Warnung`,"views.settings.ddnsDescription":`Halten Sie Hostnamen auf die öffentliche Adresse dieser Maschine gerichtet, egal welcher Registrar sie hält.`,"views.settings.defaultsDescription":`Gelten für jeden Eintrag in diesem Arbeitsbereich, der keinen eigenen Wert setzt.`,"views.settings.description":`Server-Standardwerte, Protokollaufbewahrung, Benachrichtigungen und Dynamic DNS für {workspace}.`,"views.settings.logsDescription":`Wie viel Ausgabe dieser Arbeitsbereich auf der Platte behält und wie lange.`,"views.settings.navLabel":`Abschnitte der Arbeitsbereich-Einstellungen`,"views.settings.notificationsDescription":`Absturz- und Gesundheitsalarme für diesen Arbeitsbereich, an einen Telegram-Chat gesendet.`,"views.settings.saveFailed":`Die Einstellungen konnten nicht gespeichert werden.`,"views.settings.saved":`Arbeitsbereich-Einstellungen gespeichert.`,"views.settings.savedToast":`Arbeitsbereich-Einstellungen gespeichert`,"views.settings.sectionDdns":`Dynamic DNS`,"views.settings.sectionDefaults":`Server-Standardwerte`,"views.settings.sectionLogs":`Protokolle`,"views.settings.sectionNotifications":`Benachrichtigungen`,"workspace.new":`Neuer Arbeitsbereich`,"workspace.rename":`Arbeitsbereich umbenennen`,"workspace.create":`Arbeitsbereich erstellen`,"workspace.renameAction":`Umbenennen`,"workspace.createHint":`Eigene Einstellungen, Server, Protokolle, Geheimnisse und Sicherungseintrag — von diesem selben Panel ausgeliefert.`,"workspace.renameHint":`Nur der Name ändert sich; die Arbeitsbereich-ID bleibt, also funktionieren URLs und Pfade weiter.`,"workspace.nameHint":`Wird in der oberen Leiste angezeigt.`,"workspace.nameHintId":`Wird in der oberen Leiste angezeigt. Id: {id}`,"workspace.none":`Kein Arbeitsbereich`,"workspace.delete":`Arbeitsbereich löschen`,"days.remaining":{one:`{count} Tag`,other:`{count} Tage`},"editor.unsaved":{one:`ungespeicherte Änderung`,other:`ungespeicherte Änderungen`},"editor.saveChanges":{one:`{count} Änderung speichern`,other:`{count} Änderungen speichern`},"logs.lines":{one:`{count} Zeile`,other:`{count} Zeilen`},"palette.results":{one:`{count} Ergebnis`,other:`{count} Ergebnisse`},"proxy.retry.inMinutes":{one:`~{count} Minute`,other:`~{count} Minuten`},"proxy.retry.inHours":{one:`~{count} Stunde`,other:`~{count} Stunden`},"servers.total":{one:`{count} Server`,other:`{count} Server`},"servers.running":{one:`{count} läuft`,other:`{count} laufen`},"settings.backup.entriesCount":{one:`{count} Eintrag`,other:`{count} Einträge`},"settings.backup.itemsSelected":{one:`{count} Element ausgewählt`,other:`{count} Elemente ausgewählt`},"settings.ddns.hostnameCount":{one:`{count} Hostname`,other:`{count} Hostnamen`},"settings.shell.fieldsChanged":{one:`Feld geändert`,other:`Felder geändert`},"settings.shell.saveChanges":{one:`{count} Änderung speichern`,other:`{count} Änderungen speichern`},"views.globalOverview.needLook":{one:`{count} Server braucht einen Blick`,other:`{count} Server brauchen einen Blick`},"views.logs.filesOnDisk":{one:`{count} Datei`,other:`{count} Dateien`},"views.overview.needLook":{one:`{count} Server braucht einen Blick`,other:`{count} Server brauchen einen Blick`}};export{e as default};
|