Update night: Phases 2-4 evidence — both engines, the unattended HOLD, and five new findings
gates / gates (push) Successful in 27s
gates / gates (push) Successful in 27s
Evidence off the machine at the end of the phases that produced it (R-320). Teardown follows. PHASE 2 — the two database engines, through the REAL Update button: - MariaDB 11.6 -> 12.3 on nextcloud: PROVEN, and pressed through the button for the first time. All four SPIKE-r459 observables: the datadir's own record moved 11.6.2 -> 12.3.3; the engine itself says "already upgraded ... no need to run mariadb-upgrade again"; the entrypoint says "Major version upgrade detected ... Check required!" and then STARTED and FINISHED it (not the `skipped due to $MARIADB_AUTO_UPGRADE` line R-459 feared); and the engine took its own pre-upgrade backup, 631 905 B. The seeded Nextcloud account read back. - PostgreSQL 16 -> 17 on docmost: FAILED exactly as R-463 predicted and nobody had measured. 5.1 s to held; the pin named 17 while nothing ran; the restore brought it back in 29.1 s. The engine's REFUSAL LINE was destroyed by failAndHold before any probe could read it, so it was REPRODUCED INDEPENDENTLY with a control on every step (R-320). PHASE 3 — the bad days. B1 produced THE UNATTENDED HOLD, which this project has never had: the caller pressed once with nobody watching, the app held after 312.9 s, and passes 2 and 3 pressed nothing. B2 put the pin back on a pull failure in 1.0 s. B3 refused `busy` six times. B4 showed there is NO single-flight — 5 of 5 updates ran at once and all ended honest. B5 cut the power in `backing-up` and the box recovered itself and said so. B7 refused under the 2 GB floor. B9 found R-458's risk narrower than the row states. PHASE 4 — every badge on the box is TRUE, and the held app answers all four of Q4's questions. FINDINGS, five new and three corrections to existing rows. The one that matters: R-618 is P1 — two templates name a health probe the app does not answer, and because the guarded update waits on that same probe, a SUCCESSFUL update ends by STOPPING a working app. Measured: tandoor served HTTP 200 on the new version at four samples across five minutes and was then stopped. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
R-618 ESCALATION — measured live, 2026-09-21, guest 9202, controller v0.261.0
|
||||
=============================================================================
|
||||
|
||||
tandoor's guarded Update from 2.6.13 to 2.6.15 was pressed at 21:16:47 and entered `verifying` at
|
||||
21:17:46. While it sat there, the app was asked the same question the household would ask, through
|
||||
the household's own front door:
|
||||
|
||||
21:18:28 curl -H 'Host: recipes.enkisfelhom.hu' https://192.168.0.114/accounts/login/
|
||||
-> HTTP 200
|
||||
|
||||
21:18:28 docker ps: tandoor ghcr.io/tandoorrecipes/recipes:2.6.15 Up 25 seconds
|
||||
|
||||
So the NEW version was installed and SERVING. The controller nevertheless could not see it, because
|
||||
`.felhom.yml` probes `type: http, port: 8080` and the tandoor container listens on **80 only**.
|
||||
|
||||
The consequence is the part that matters, and it is bigger than a wrong label:
|
||||
|
||||
* `verifying` cannot pass, so the full `update.health_timeout` (5 minutes) is spent.
|
||||
* `Manager.failAndHold` then runs `compose down` and the app is STOPPED.
|
||||
* The household is told the update failed and is sent to a restore they do not need.
|
||||
* Their recipe app — which was working, on the new version, with its data intact — is DOWN.
|
||||
|
||||
Nothing is lost: the data is in the volumes and the restore works. But a single wrong port number
|
||||
in a template turns a SUCCESSFUL update into an outage plus an unnecessary restore, every time,
|
||||
for every household running that app.
|
||||
|
||||
The same shape applies to `zipline` (probe `type: api` + `expect: status 200` on `/api/health`,
|
||||
which that app answers 404) and, unmeasured, to `wger`.
|
||||
|
||||
|
||||
A SECOND CHECK, five minutes later in the same `verifying` window
|
||||
-----------------------------------------------------------------
|
||||
21:19:52 front door=200 docker=healthy
|
||||
21:20:18 front door=200 docker=healthy
|
||||
21:20:45 front door=200 docker=healthy
|
||||
|
||||
THE END OF IT, 2026-09-21 21:22:49 (+361.9 s)
|
||||
---------------------------------------------
|
||||
phase=failed
|
||||
„A(z) tandoor frissítése 2026-09-21 21:22-kor nem sikerult, es az alkalmazas nem indult el az uj
|
||||
verzioval. Az alkalmazas biztonsagi okbol leallitva marad, hogy az adatai ne serüljenek.
|
||||
Visszaallithato a Mentesek oldalon ebbol a biztonsagi mentesbol: sajat meghajto, 2026-09-21 21:15
|
||||
— ez a masolat a beallitasokat, az adatbazist es az adatkoteteket tartalmazza."
|
||||
(quoted with ASCII fragments; the live sentence carries its accents — see apps/tandoor/)
|
||||
|
||||
docker ps -a : NO tandoor container at all
|
||||
front door : HTTP 404 (it was 200 four times in the preceding five minutes)
|
||||
pinned_images : ghcr.io/tandoorrecipes/recipes:2.6.15
|
||||
installed_images : ghcr.io/tandoorrecipes/recipes:2.6.13 <- DISAGREES with the pin
|
||||
live compose image: : ghcr.io/tandoorrecipes/recipes:2.6.15
|
||||
docker inspect : []
|
||||
|
||||
The four version observables DISAGREE after a hold, and that is CORRECT — `09` §5.2: the pin is a
|
||||
DECISION and `installed_images` is an OBSERVATION, and "when they disagree that is a signal, not a
|
||||
bug to paper over". Here the signal reads exactly right: we decided 2.6.15, the last thing actually
|
||||
observed running was 2.6.13, and nothing is running now.
|
||||
|
||||
TIMELINE, one line
|
||||
------------------
|
||||
21:16:47 Update pressed 21:17:46 verifying begins
|
||||
21:18:28 NEW VERSION SERVING 200, docker healthcheck green
|
||||
21:19:52 200 / healthy 21:20:18 200 / healthy 21:20:45 200 / healthy
|
||||
21:22:49 health timeout -> failAndHold -> compose down -> app STOPPED, front door 404
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
R-606 CONFIRMED ON THE HOLD SENTENCE — 2026-09-21, guest 9202, controller v0.261.0
|
||||
==================================================================================
|
||||
|
||||
R-606 was found on the INTERRUPTED sentence. This is the same defect on the HOLD sentence, which
|
||||
R-606's own text ranks highest because it is "a PROMISE ABOUT WHETHER THE CUSTOMER'S FILES COME
|
||||
BACK". Read off /apps/adventurelog?lang=en while the app was held.
|
||||
|
||||
EVERYTHING AROUND IT IS CORRECTLY ENGLISH — positive control:
|
||||
"Launcher Dashboard Apps Storage Drives Network storage Backup Overview Remote backup"
|
||||
"An installed app is not running: AdventureLog (stopped) System monitor ->"
|
||||
"An installed app is not running: Tandoor Recipes (stopped) System monitor ->"
|
||||
"The hub connection is off - central monitoring is not running"
|
||||
"AdventureLog Stopped Update available - today Open Log Export Settings"
|
||||
"Move to another storage You can move this app's data to another connected storage."
|
||||
|
||||
THE TWO SENTENCES THAT MATTER ARE HUNGARIAN — on the English page, verbatim:
|
||||
"A(z) adventurelog frissitese 2026-09-21 20:53-kor nem sikerult, es az alkalmazas nem indult el
|
||||
az uj verzioval."
|
||||
"Visszaallithato a Mentesek oldalon ebbol a biztonsagi mentesbol: sajat meghajto, 2026-09-21
|
||||
20:47 - ez a masolat a beallitasokat, az adatbazist es az adatkoteteket tartalmazza."
|
||||
(quoted here with ASCII fragments; the live page carries the accents - see
|
||||
apps/adventurelog/held-page-en.html)
|
||||
|
||||
So an English household is told, in English, that their app is stopped and that an update is
|
||||
available - and told in HUNGARIAN what actually happened, which copy can bring their data back,
|
||||
when it was taken, and WHAT IS INSIDE IT.
|
||||
|
||||
NEGATIVE CONTROL: the Hungarian page carries the same two sentences and the chrome in Hungarian,
|
||||
so the sentences are not "missing a translation" - they are not translatable at all, because
|
||||
`Stack.UpdateError` and the hold text are finished STRINGS rather than keys (R-606's mechanism).
|
||||
|
||||
ONE MORE THING THIS READING SETTLES, and it is for `09` Q4: the box DOES surface a held app
|
||||
everywhere, not only on its own page. The banner "An installed app is not running: AdventureLog
|
||||
(stopped)" appeared at the TOP OF EVERY authenticated page, for both held apps at once.
|
||||
@@ -0,0 +1,58 @@
|
||||
# Phase 2.1 — MariaDB across a major, through the REAL Update button. PROVEN.
|
||||
|
||||
**The first time this has ever been pressed through the product.** R-459 measured the conversion on
|
||||
the harness; R-469 lifted the gate on 2026-09-21 morning; the BUTTON had never been used for it.
|
||||
|
||||
**Venue:** guest 9202, controller v0.261.0. **App:** `nextcloud` — the app image does NOT move
|
||||
(`nextcloud:34.0.1-apache` throughout); only the `mariadb:` sidecar, **11.6 → 12.3**.
|
||||
|
||||
**Seeded and read back through the app's own CLI:** `occ user:add` created `drillb9a9d0`
|
||||
("The account … was created successfully"), `occ user:info` found it **before** the move (control C1)
|
||||
and **after** it, each time with the fixture's own negative control — a uid that cannot exist must
|
||||
read as absent — passing.
|
||||
|
||||
## The four observables of `SPIKE-r459-mariadb-upgrade-2026-09-06`
|
||||
|
||||
| # | observable | before | after |
|
||||
|---|---|---|---|
|
||||
| 1 | `mariadb_upgrade_info` — the datadir's own record | `11.6.2-MariaDB` | **`12.3.3-MariaDB`** |
|
||||
| 2 | the engine's OWN check (R-464: never the log line) | *"Access denied"* — the probe was unauthenticated, see the note below | **„This installation of MariaDB is already upgraded to 12.3.3-MariaDB. There is no need to run mariadb-upgrade again."** |
|
||||
| 3 | the entrypoint's own lines | — | **„Major version upgrade detected from 11.6.2-MariaDB to 12.3.3-MariaDB. Check required!"** → „Starting mariadb-upgrade" → „Finished mariadb-upgrade" |
|
||||
| 4 | the engine's own pre-upgrade backup | absent | **`/var/lib/mysql/system_mysql_backup_11.6.2-MariaDB.sql.zst`, 631 905 bytes** |
|
||||
| 5 | the version actually running | `11.6.2-MariaDB-ubu2404` | **`12.3.3-MariaDB-ubu2404`** |
|
||||
| 6 | container | `mariadb:11.6` running, 0 restarts | **`mariadb:12.3` running, 0 restarts** |
|
||||
|
||||
**Observable 3 is the one that matters most**, because R-459's whole finding was that MariaDB can
|
||||
apply a major and *skip* the conversion quietly, printing `skipped due to $MARIADB_AUTO_UPGRADE`.
|
||||
That line is **absent**; the entrypoint says the conversion was **detected, started and finished**.
|
||||
|
||||
**And observable 2 confirms R-464's own warning about the instrument:** the command exits **1** when
|
||||
it means *no upgrade is needed*. The exit code carries no usable information here; the SENTENCE is
|
||||
the discriminator, which is exactly what R-464 says.
|
||||
|
||||
## The four version observables, all agreeing
|
||||
|
||||
pinned nextcloud:34.0.1-apache · mariadb:12.3 · redis:7-alpine
|
||||
installed nextcloud:34.0.1-apache · mariadb:12.3 · redis:7-alpine
|
||||
compose nextcloud:34.0.1-apache · mariadb:12.3 · redis:7-alpine
|
||||
inspect nextcloud-db mariadb:12.3 running=true restarts=0
|
||||
nextcloud nextcloud:34.0.1-apache running=true restarts=0
|
||||
nextcloud-redis redis:7-alpine running=true restarts=0
|
||||
|
||||
## Two honest notes on the instrument
|
||||
|
||||
1. **The first `engine-before.txt` capture asked the engine without credentials** and got
|
||||
`ERROR 1045 Access denied` where observable 2 should be. The probe was corrected mid-run to pass
|
||||
`--user=root --password=$MARIADB_ROOT_PASSWORD` (the recipe `upgrade-test.py`'s own
|
||||
`ENGINE_PROBES` already uses) and the AFTER capture above was retaken with it. The BEFORE value
|
||||
for observable 2 is therefore **not measured**, and is stated as such rather than inferred from
|
||||
observable 1.
|
||||
2. The `ls` in observable 4 printed `(no pre-upgrade backup file present)` **after** listing the
|
||||
file, because it globs two patterns and only one matched. The file is there; the fallback text is
|
||||
the shell's, not the engine's. Noted so nobody reads the line as a contradiction.
|
||||
|
||||
## What this settles
|
||||
|
||||
The MariaDB half of `09` §3 decision 5 and R-469's lift is now proven **through the button a
|
||||
household presses**, not only on the harness — conversion detected, run, finished, the engine's own
|
||||
pre-upgrade backup taken, and the customer's account still there afterwards.
|
||||
@@ -0,0 +1,51 @@
|
||||
*** THIS FILE IS THE FIRST ATTEMPT AND ITS MAIN RESULT IS WRONG — KEPT DELIBERATELY. ***
|
||||
|
||||
The volume lookup returned EMPTY, so the copy was empty, so postgres:17 initialised a FRESH 17
|
||||
datadir and started happily. The line "PG_VERSION on the copy:" two lines below is blank and that
|
||||
should have stopped the step; it did not. `17-...` is therefore NOT evidence that 17 accepts a 16
|
||||
datadir — it never met one.
|
||||
|
||||
WHAT IT DID ACCIDENTALLY MEASURE, and this part IS real and useful: the MIRROR case. postgres:16
|
||||
meeting a datadir written by 17 refuses in its own words —
|
||||
|
||||
FATAL: database files are incompatible with server
|
||||
DETAIL: The data directory was initialized by PostgreSQL version 17, which is not compatible
|
||||
with this version 16.15.
|
||||
|
||||
which is the sentence a household would meet if a 17 pin were ever put BACK to 16. The correct
|
||||
measurement — 17 meeting 16 — is in `18-postgres-refusal-reproduced.txt`.
|
||||
|
||||
================================================================================================
|
||||
|
||||
INDEPENDENT REPRODUCTION of the PostgreSQL 16 -> 17 refusal
|
||||
R-320: the product destroyed the original evidence (R-621), so it is reproduced here rather
|
||||
than inferred. Plain docker, beside the product, on a COPY of a real 16 datadir.
|
||||
|
||||
the live docmost 16 datadir volume:
|
||||
PG_VERSION on the copy:
|
||||
|
||||
=== now start postgres:17-alpine on that 16 datadir — the household's case, exactly
|
||||
--- container state:
|
||||
running=true exit=0 restarts=0
|
||||
--- THE REFUSAL, VERBATIM:
|
||||
2026-09-21 19:45:47.148 UTC [41] LOG: database system is shut down
|
||||
done
|
||||
server stopped
|
||||
|
||||
PostgreSQL init process complete; ready for start up.
|
||||
|
||||
2026-09-21 19:45:47.239 UTC [1] LOG: starting PostgreSQL 17.11 on x86_64-pc-linux-musl, compiled by gcc (Alpine 15.2.0) 15.2.0, 64-bit
|
||||
2026-09-21 19:45:47.239 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
|
||||
2026-09-21 19:45:47.239 UTC [1] LOG: listening on IPv6 address "::", port 5432
|
||||
2026-09-21 19:45:47.245 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
|
||||
2026-09-21 19:45:47.253 UTC [55] LOG: database system was shut down at 2026-09-21 19:45:47 UTC
|
||||
2026-09-21 19:45:47.263 UTC [1] LOG: database system is ready to accept connections
|
||||
|
||||
--- positive control: the SAME copy under postgres:16-alpine must start
|
||||
running=false exit=1
|
||||
|
||||
2026-09-21 19:45:53.852 UTC [1] FATAL: database files are incompatible with server
|
||||
2026-09-21 19:45:53.852 UTC [1] DETAIL: The data directory was initialized by PostgreSQL version 17, which is not compatible with this version 16.15.
|
||||
--- and the data is still there, asked of the engine:
|
||||
Error response from daemon: container ea0332381c9771265f47fd08988836c38afb697496096074305ed154a3ac8f3e is not running
|
||||
(throwaway containers and the copied volume removed by name)
|
||||
@@ -0,0 +1,32 @@
|
||||
=== 0. find the live docmost 16 datadir, and PROVE it is 16 before touching anything
|
||||
mounts of docmost-postgres:
|
||||
volume docmost_docmost_postgres_data /var/lib/docker/volumes/docmost_docmost_postgres_data/_data /var/lib/postgresql/data
|
||||
volume: docmost_docmost_postgres_data
|
||||
PG_VERSION of the SOURCE: '16'
|
||||
|
||||
=== 1. copy it, and PROVE the copy is 16 too
|
||||
copied
|
||||
PG_VERSION of the COPY: '16'
|
||||
size of the copy: 49.0M
|
||||
|
||||
=== 2. THE MEASUREMENT: postgres:17-alpine on that 16 datadir — the household's exact case
|
||||
container: running=false exit=1 restarts=0
|
||||
--- POSTGRESQL 17's OWN WORDS, VERBATIM:
|
||||
|
||||
PostgreSQL Database directory appears to contain a database; Skipping initialization
|
||||
|
||||
2026-09-21 22:11:32.601 CEST [1] FATAL: database files are incompatible with server
|
||||
2026-09-21 22:11:32.601 CEST [1] DETAIL: The data directory was initialized by PostgreSQL version 16, which is not compatible with this version 17.11.
|
||||
--- the copy's PG_VERSION AFTER 17 refused it (must still be 16 — nothing was migrated):
|
||||
16
|
||||
|
||||
=== 3. POSITIVE CONTROL: the same copy under postgres:16-alpine must start AND hold the data
|
||||
container: running=true exit=0
|
||||
2026-09-21 22:11:43.497 CEST [33] LOG: checkpoint starting: end-of-recovery immediate wait
|
||||
2026-09-21 22:11:43.583 CEST [33] LOG: checkpoint complete: wrote 600 buffers (3.7%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.014 s, sync=0.061 s, total=0.089 s; sync files=409, longest=0.025 s, average=0.001 s; distance=2838 kB, estimate=2838 kB; lsn=0/1BEB798, redo lsn=0/1BEB798
|
||||
2026-09-21 22:11:43.592 CEST [1] LOG: database system is ready to accept connections
|
||||
--- the DATA, asked of the engine itself:
|
||||
tables in the public schema: 48
|
||||
|
||||
=== 4. teardown, BY NAME
|
||||
removed: DRILL-pg17-refuse, DRILL-pg16-ok, volume DRILL-pg16-copy
|
||||
@@ -0,0 +1,59 @@
|
||||
# `09` §3b Q4 — the unattended HOLD. MEASURED 2026-09-21.
|
||||
|
||||
Q4 asks: *an automatic update that ends HELD happened while everyone was asleep — who is told, when,
|
||||
and does the box try again?* Until tonight **the hold half had never been produced unattended**, and
|
||||
the 2026-09-21 afternoon session recorded exactly why: the only failing edge available was
|
||||
`vikunja → alpine:3.20`, which the within-a-major rule correctly refuses before the guarded update is
|
||||
ever reached. *The rule that makes automatic updates safe is the same rule that refuses the obvious
|
||||
way to break one.*
|
||||
|
||||
## What made it measurable
|
||||
|
||||
A throwaway image store on the scratch guest, and an edge the safety rule **cannot** filter out:
|
||||
|
||||
localhost:5000/drill/glance:1.0.0 the real glance image, retagged — it serves
|
||||
localhost:5000/drill/glance:1.0.1 starts, stays up, and NEVER serves
|
||||
|
||||
Same repository, same major, plain version tags, so `stacks.CompareImageRefs` orders them and the
|
||||
caller presses. Proven beforehand in isolation: the `1.0.1` container runs 14 s with **0 restarts**
|
||||
and refuses every connection on 8080.
|
||||
|
||||
## The measurement, verbatim
|
||||
|
||||
`unattended-caller.py`, three passes 90 s apart. **Nobody pressed anything.**
|
||||
|
||||
2026-09-21T19:47:49Z === pass 1/3
|
||||
2026-09-21T19:47:49Z glance BEHIND and within a major — pressing Update
|
||||
2026-09-21T19:47:49Z glance: phase=safety-dump updating=True
|
||||
2026-09-21T19:47:51Z glance: phase=verifying updating=True
|
||||
2026-09-21T19:53:02Z glance: phase=failed updating=False
|
||||
2026-09-21T19:53:02Z glance ENDED held after 312.9s
|
||||
2026-09-21T19:54:32Z === pass 2/3
|
||||
2026-09-21T19:56:03Z === pass 3/3
|
||||
2026-09-21T19:56:03Z === summary outcomes={'glance': ('held', 312.9)} never_again=['glance']
|
||||
|
||||
## What Q4 now knows
|
||||
|
||||
| Q4's question | the answer, measured |
|
||||
|---|---|
|
||||
| does an unattended update ever produce a HOLD? | **Yes** — 312.9 s, the full `health_timeout` plus the phases |
|
||||
| does the box try again? | **No.** Passes 2 and 3 pressed nothing at all; `never_again=['glance']` |
|
||||
| is the household told? | **On the screen, yes** — the app page carries the sentence, and the banner „Telepített alkalmazás nem fut: …" appears at the top of **every** authenticated page |
|
||||
| told what, exactly? | what happened, **when** (`21:53`), **which copy** („saját meghajtó, 2026-09-21 21:47"), and **what that copy holds** („a beállításokat, az adatbázist és az adatköteteket") |
|
||||
| by MAIL? | **UNMEASURABLE on this venue** — `hub.enabled: false` and the notifier returns before it logs (R-620). Q4's mail half is still unmeasured |
|
||||
| in ENGLISH? | **No.** The sentence is Hungarian on the English page (R-606, confirmed tonight on the hold sentence itself) |
|
||||
|
||||
## The one qualification, stated plainly
|
||||
|
||||
**This is the hold produced by a caller that presses the existing guarded Update.** It is not Slice 6,
|
||||
which does not exist. What it proves is that the MECHANISM Q4's recommended option rests on — press
|
||||
once, hold honestly, tell the household on the page, do not retry — **is already there and already
|
||||
behaves that way**, and that the remaining work in Q4 is the MAIL and the ENGLISH, not the hold.
|
||||
|
||||
## And one thing that nearly went unmeasured
|
||||
|
||||
`unattended-caller.py`'s `follow()` read `update_phase` and `updating` off the API **envelope**, so
|
||||
both were always `None`, and **every update it followed would have run to the 900 s timeout and been
|
||||
recorded `timeout` rather than `held`** (R-623). It was found by reading the file before relying on
|
||||
it, and fixed. The log above — with its real phase transitions — is what the fixed version produces.
|
||||
The broken one would have written `phase=None` four times and then `timeout`.
|
||||
@@ -0,0 +1,40 @@
|
||||
WHICH UPDATE REFUSALS ACTUALLY REACH AN ENGLISH HOUSEHOLD IN ENGLISH
|
||||
2026-09-21, guest 9202, controller v0.261.0. `langFor` DOES honour ?lang= (api/i18n_api.go:30).
|
||||
|
||||
glance hu http=409 reason=held :: A(z) glance frissítése 2026-09-21 21:53-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva mar
|
||||
glance en http=409 reason=held :: A(z) glance frissítése 2026-09-21 21:53-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva mar
|
||||
|
||||
adventurelog hu http=409 reason=not_deployed :: Az alkalmazás nincs telepítve, ezért nem frissíthető.
|
||||
adventurelog en http=409 reason=not_deployed :: Az alkalmazás nincs telepítve, ezért nem frissíthető.
|
||||
|
||||
nosuchapp hu http=404 reason=not_found :: stack "nosuchapp" not found
|
||||
nosuchapp en http=404 reason=not_found :: stack "nosuchapp" not found
|
||||
|
||||
|
||||
WHAT THIS CORRECTS
|
||||
==================
|
||||
R-606's own text records that controller v0.260.0 "routed the 409 REFUSAL through `errText` (so the
|
||||
pre-flight refusals reach an English household in English)". **That is measured FALSE for at least
|
||||
three refusals**, and the reason is mechanical rather than a regression:
|
||||
|
||||
* the ROUTING is there — `Router.langFor` honours `?lang=` (`api/i18n_api.go:30-40`) and
|
||||
`errText` calls it, so an English request IS asked for in English;
|
||||
* but the SENTENCES are not keys. `MsgUpdateDiskFmt`, `MsgUpdateNotDeployed`, `MsgUpdateBusy` and
|
||||
their siblings (`stacks/update.go` L81-96) are **finished Hungarian string constants**, raised
|
||||
with `fmt.Sprintf` into `refuseUpdate`. `errText` renders "its own text" for an error that
|
||||
carries no bundle message — which is exactly right, and which means routing them through it
|
||||
changes nothing at all.
|
||||
|
||||
So v0.260.0 built the pipe and the old sentences never entered it. Only the sentences BORN as keys
|
||||
(v0.260.0's `downgrade`, v0.261.0's `self_updating`) actually translate.
|
||||
|
||||
**Why this matters more than a cosmetic:** the `held` refusal is the one that tells a household
|
||||
which copy can bring their data back and what is inside it. An English household pressing Update on
|
||||
a held app is told, in Hungarian, why they cannot.
|
||||
|
||||
MEASURED, all three with the Hungarian request as the control:
|
||||
held -> identical Hungarian in both languages
|
||||
not_deployed -> identical Hungarian in both languages
|
||||
disk -> identical Hungarian in both languages ("Nincs eleg szabad hely a frissiteshez:
|
||||
1.4 GB szabad, az uj verzio letolteshez legalabb 2 GB szukseges.")
|
||||
not_found -> English in both, because it is a Go error and not ours (correct)
|
||||
@@ -0,0 +1,41 @@
|
||||
# B9 — a FROZEN app receives a newer `.felhom.yml` (R-458). MEASURED, and it REFINES the row.
|
||||
|
||||
**Setup.** `bentopdf` frozen: installed `ghcr.io/alam00000/bentopdf:v2.8.6`, catalog
|
||||
`localhost:5000/drill/pdf:1.0.1`. A `.felhom.yml`-ONLY change was pushed to the drill catalog — a
|
||||
health check for a path only a newer version would serve, `/__drill_only_in_the_newer_version__`.
|
||||
No `image:` line was touched.
|
||||
|
||||
## §5.4's deliberate asymmetry, confirmed live
|
||||
|
||||
On the box, after one sync:
|
||||
|
||||
.felhom.yml checks: - type: http, port: 8080, path: /__drill_only_in_the_newer_version__
|
||||
- type: http, port: 8080 <- the original
|
||||
compose image: ghcr.io/alam00000/bentopdf:v2.8.6 <- UNCHANGED
|
||||
|
||||
So the `.felhom.yml` kept flowing to a frozen app while the compose file stayed frozen, exactly as
|
||||
`09` §5.4 says and for the reason it gives (the file carries `catalog_since`, which the badge needs).
|
||||
|
||||
## R-458's claim holds — and the reason is NARROWER than the row states
|
||||
|
||||
Ten samples over two minutes, every one:
|
||||
|
||||
state = running front door = HTTP 200
|
||||
|
||||
**No false alarm was produced at all**, and the reason is the probe's own semantics rather than
|
||||
luck. `healthprobe.go:258-261`: for `type: http`, **any response is healthy**. The bogus path
|
||||
answers `404` — which is a response. So a health check written for a newer version **cannot** make
|
||||
a `type: http` app read degraded, however wrong the path is.
|
||||
|
||||
**The refinement R-458 should carry:** its false-alarm risk is real only for `type: api` probes that
|
||||
carry an `expect` block, where the status is compared. Of the 53 templates, that is the minority —
|
||||
and it is the same subset R-618 convicts `zipline` in. For every `type: http` template and every
|
||||
`type: api` WITHOUT `expect`, a path that a newer version would serve is simply invisible to the
|
||||
probe.
|
||||
|
||||
**And the direction is confirmed, which is the row's actual claim:** the failure direction is a
|
||||
false alarm, never data loss. Nothing touched the app's data; the app never stopped; the card showed
|
||||
the previous pull failure's sentence („Az új verzió letöltése nem sikerült, ezért a frissítés
|
||||
elmaradt.") and nothing new.
|
||||
|
||||
**Teardown:** the `.felhom.yml` was reverted in the drill catalog in the same leg.
|
||||
@@ -0,0 +1,27 @@
|
||||
B8 — THE FLOATING PIN, re-read cleanly (the first read caught docmost mid-update)
|
||||
2026-09-21, guest 9202, controller v0.261.0
|
||||
|
||||
installed_images, as the box records them:
|
||||
docmost ref=docmost/docmost:0.96.0
|
||||
digest=sha256:b56947fcfd08aab8fae12a377e1792784786adbf8b96e4281f14ef4fc072685a
|
||||
first seen at=2026-09-21T20:11:02Z
|
||||
docmost-postgres ref=postgres:16-alpine
|
||||
digest=sha256:721873c34ceb9f8d8fc265984940dc982404c105f19ad51be9fdc5970a6080ea
|
||||
first seen at=2026-09-21T20:11:02Z
|
||||
docmost-redis ref=redis:7-alpine
|
||||
digest=sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499
|
||||
first seen at=2026-09-21T20:11:02Z
|
||||
|
||||
catalog_images: {'docmost': 'docmost/docmost:0.96.0', 'docmost-postgres': 'postgres:16-alpine', 'docmost-redis': 'redis:7-alpine'}
|
||||
badge HU: ['Naprakész']
|
||||
badge EN: ['Up to date']
|
||||
|
||||
postgres:16-alpine
|
||||
upstream digest, measured from DooPlex tonight : sha256:721873c34ceb9f8d8fc265984940dc982404c105f19ad51be9fdc5970a6080ea
|
||||
the digest THIS BOX records for what it runs : sha256:721873c34ceb9f8d8fc265984940dc982404c105f19ad51be9fdc5970a6080ea
|
||||
SAME? True -> the badge is TRUE for this box
|
||||
|
||||
redis:7-alpine
|
||||
upstream digest, measured from DooPlex tonight : sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499
|
||||
the digest THIS BOX records for what it runs : sha256:858f009f9709ce576febc734aa78b8f6d624b82571f9ddb6bda4377c833b3499
|
||||
SAME? True -> the badge is TRUE for this box
|
||||
@@ -0,0 +1,35 @@
|
||||
# The alarm truth table for the update night, scored against `08-alarm-ladder.md`
|
||||
|
||||
**READ THE LIMIT FIRST, because it bounds every row.** Guest 9202 runs with `hub.enabled: false`.
|
||||
Every notifier entry point returns **before it logs anything** (`notify/notifier.go:269, :359, :917,
|
||||
:959, :1058`), so **no hub event and no customer mail could be produced or observed tonight**. The
|
||||
hub was not enabled on 9202 to get around this: that would register an unclaimed host at the live
|
||||
hub and could mail a real address, and the brief fences the hub. Filed as **R-620**.
|
||||
|
||||
So this table scores the surfaces that DO exist here — what the household READS (the app page, the
|
||||
dashboard, the backups page) and what the box's own log records — and says "unmeasurable here" for
|
||||
the send side rather than leaving a blank that could be read as "nothing fired".
|
||||
|
||||
| # | what happened | should it alarm, per `08` | did the box classify it that way | what the household could READ | verdict |
|
||||
|---|---|---|---|---|---|
|
||||
<!-- ROWS -->
|
||||
|
||||
## The three things this table is for
|
||||
|
||||
**1. Which alarm fired and was it true.** <FIRED>
|
||||
|
||||
**2. Which should have fired and did not.** <MISSING>
|
||||
|
||||
**3. The one that is structurally invisible on this venue.** Every row's "who was told" column is
|
||||
answered only for the SCREEN. `08` §6.1's severity contract and §6.2's delivery grain were not
|
||||
exercised at all, and no row below may be read as evidence about them.
|
||||
|
||||
## A note on `unhealthy`, because two of tonight's findings turn on it
|
||||
|
||||
`08` §4 puts `unhealthy` deliberately in the **NOT-down** set — *"a running container whose
|
||||
healthcheck is failing. Folding it in reintroduces the flapping fix-3 was written to stop."* That
|
||||
ruling is why tandoor and zipline reading `unhealthy` all night produced **no alarm at all**, and it
|
||||
is the right ruling. **But R-618 shows the cost of the other half of that decision:** the same probe
|
||||
result that is correctly ignored by the alarm ladder is NOT ignored by the guarded update's
|
||||
`verifying` phase, which waits on it and then stops the app. One probe, two consumers, opposite
|
||||
tolerances — and nothing says so in either document.
|
||||
@@ -0,0 +1,39 @@
|
||||
# Draft — the line to append to the capability map's UPDATE row (`00-capability-map.md` line 105)
|
||||
|
||||
*(Appended to the existing "Update is GUARDED …" row's evidence column. `<…>` filled at the end.)*
|
||||
|
||||
---
|
||||
|
||||
**WIDENED 2026-09-21 (the update night) from 3 apps to `<N>`, and NARROWED in one place by the same
|
||||
run.** `audits/DRILL-update-night-2026-09-21.md`. On scratch guest 9202 (controller v0.261.0),
|
||||
against a **private drill catalog** so the live catalog carried no test reference at any point,
|
||||
`<N>` real within-a-major upstream edges were walked through the product's own guarded Update, each
|
||||
app seeded and read back **through its own front door** (R-156) with a negative control on every
|
||||
readback: **`<P>` proven, `<F>` failed, `<I>` inconclusive.**
|
||||
|
||||
**What the PROVEN edges prove, precisely:** the app moved, the four version observables agreed, and
|
||||
the data the app itself was given came back through the app's own interface afterwards. `<MIG>` of
|
||||
them printed a verbatim migration line.
|
||||
|
||||
**What the FAILED edges prove, and they are the more valuable half.** `<FAILED_LIST>`.
|
||||
`adventurelog v0.12.1 → v0.13.0` applied **nine database migrations successfully** and then never
|
||||
bound its port; the update held after the full health wait, the hold sentence named the tier, the
|
||||
date and what the copy holds, and the restore the sentence names brought the app back. **That is
|
||||
this row's own promise, exercised on a real upstream edge rather than a staged one.**
|
||||
|
||||
**AND THE NARROWING, which this row must carry because it is the same mechanism:** the `verifying`
|
||||
phase trusts the `.felhom.yml` probe absolutely, and **two of the 53 templates name a probe the app
|
||||
does not answer** — `tandoor` (port 8080; it listens on 80) and `zipline` (`/api/health`; it answers
|
||||
404 there, while the compose healthcheck in the same file uses `/api/healthcheck` and is green).
|
||||
For those apps a **successful** update is stopped by its own health wait: tandoor was measured
|
||||
**serving HTTP 200 on the new version at four samples across five minutes**, with docker's own
|
||||
healthcheck green, and was then stopped by `failAndHold` and the household sent to a restore they
|
||||
did not need. **R-618, P1.** No data was lost and the restore works — but "the update is guarded"
|
||||
must not be read as "the guard is right about whether the app came up".
|
||||
|
||||
**Still true and unchanged:** no automatic rollback (by measurement); the route back is the restore;
|
||||
a multi-major jump ends held honestly.
|
||||
|
||||
**Not measured on this venue, and named rather than assumed:** every event and every customer mail.
|
||||
Guest 9202 runs `hub.enabled: false` and the notifier returns before it logs (**R-620**), so the
|
||||
whole "who was told" half of `08` was structurally unobservable tonight.
|
||||
File diff suppressed because one or more lines are too long
@@ -26,3 +26,26 @@ A resuming session reads THIS FILE FIRST and never repeats a finished step.
|
||||
| 21:08 | CI check for the catalog push | **GREEN** — job **id=830**, `name='gates'`, `status='completed'`, `conclusion='success'`, matched on `head_sha=4463243f2e09`. Confirms CLAUDE.md's warning: page 1's newest id was 52, the real newest was 830 — job ids are NOT page-ordered | scratchpad/ci.sh |
|
||||
| 21:12 | **Observation, NOT a new row** — an app with an `HDD_PATH` cannot have its DATA removed on 9202 | R-442's guard working as designed: `/api/disks` answers `agent not configured` on this guest, so the drive path cannot be RESOLVED and the removal is **refused with the app kept** rather than half-deleted. The household's other choice — remove the app, KEEP the data — is accepted. The harness now takes that route and tidies its own directories by name at teardown | apps/navidrome/, apps/romm/ |
|
||||
| 21:13 | **FINDING R-623** — the unattended caller turned every SUCCESS into a `timeout` | Read before use, not after: `follow()` read `update_phase`/`updating` off the API ENVELOPE, so both were always `None`, every followed update hit the 900 s timeout and was then marked never-press-again. **Fixed in that file** before B1 relied on it. The earlier night missed it because its only `follow()` pass was the one whose log was lost | update-arc-gaps-2026-09-21/unattended-caller.py |
|
||||
| 21:18 | **Interim commit pushed** — felhom.eu `da20722e76a7` | Phase 0 + Phase 1 evidence off the machine before Phases 2-5 (R-320). repo_gates.py --fast: all 15 OK. Includes the two instrument fixes (api-recipe route, unattended-caller `follow()`) | felhom.eu@da20722e76a7 |
|
||||
| 21:18 | **R-618 RAISED TO P1 by a live measurement** | tandoor 2.6.13→2.6.15 was **serving HTTP 200 on the NEW version** at 21:18:28 while the update sat in `verifying`, because the probe names port 8080 and the app listens on 80. The health wait then times out and `failAndHold` STOPS the working app. A wrong port turns every successful update into an outage plus a needless restore | 14-tandoor-serving-while-verifying.txt |
|
||||
| 21:22 | **EDGE tandoor 2.6.13 -> 2.6.15** (db-postgres) | **FAILED — and the failure is OURS, not the app's.** The new version was SERVING 200 with a green docker healthcheck at four samples across five minutes; the controller's probe names port 8080 and the app listens on 80, so `verifying` timed out and `failAndHold` STOPPED a working app. The four observables disagree exactly as `09` §5.2 predicts. **R-618 is now P1** | 14-tandoor-serving-while-verifying.txt, apps/tandoor/ |
|
||||
| 21:35 | **failwalk adventurelog — the household's WAY OUT, on a real failed edge** | **PROVEN** — the restore the hold sentence names („helyi", 1 copy offered) ran in **75 s**; app back on v0.12.1, all three containers running, hold CLEARED, state `running`. R-606 CONFIRMED on the hold sentence itself (English page, Hungarian sentence) with positive and negative controls | apps/adventurelog/failwalk.json, 15-r606-*.txt |
|
||||
| 21:36 | Observation after the restore | The hold sentence is GONE from the card (R-480 working) and the app reads „Fut". The badge briefly read „Frissítés elérhető — **65 napja**" rather than „ma", because the restore brought back the recovery unit's own `.felhom.yml` with its pre-drill `catalog_since`; the syncer copies that file verbatim every cycle, so it heals within one sync. Recorded as an observation, not a row | apps/adventurelog/failwalk.json |
|
||||
| 21:37 | **failwalk tandoor — the way out of the WRONG-PROBE hold** | **PROVEN** — restore in **32.4 s**, hold cleared, app running, data reads back. Note what this means for R-618: the household's route back works, so nothing is LOST — but they had to walk it for an update that had actually succeeded | apps/tandoor/failwalk.json |
|
||||
| 21:40 | **PHASE 2.1 — MariaDB 11.6 -> 12.3 through the REAL Update button** | **PROVEN, a first.** All four SPIKE-r459 observables: datadir `11.6.2`->`12.3.3`; the engine's own check says „already upgraded … no need to run mariadb-upgrade again"; the entrypoint says „Major version upgrade detected … Check required!" then started and **finished** (NOT the `skipped due to $MARIADB_AUTO_UPGRADE` line R-459 feared); the engine's own pre-upgrade backup `system_mysql_backup_11.6.2-MariaDB.sql.zst` 631 905 B. Seeded Nextcloud account read back. All four version observables agree | 16-phase2.1-mariadb-major.md, apps/nextcloud-engine-mariadb/ |
|
||||
| 21:45 | **PHASE 2.2 — PostgreSQL 16 -> 17 through the real Update button** | **FAILED, exactly as R-463 predicted and nobody had measured.** The update ended in **5.1 s**, the app was stopped and held, and **`pinned` named `postgres:17-alpine` while `installed` still said 16 and nothing was running** — the brief's own question answered. The restore then brought it back in **29.1 s** (hold CLEARED, health probe 200) | apps/docmost-engine-postgres/ |
|
||||
| 21:45 | **R-621 demonstrated itself on this leg's KEY artefact** | The engine's refusal line was GONE before any probe could read it — `failAndHold` had removed the container, and the controller log does not carry it either. Reproduced independently instead (R-320) | 18-postgres-refusal-reproduced.txt |
|
||||
| 21:45 | **My own first reproduction was WRONG, and is kept labelled** | The volume lookup returned empty -> the copy was empty -> postgres:17 initialised a FRESH datadir and started happily. A blank `PG_VERSION` should have stopped the step and did not. Rewritten so every step proves itself first. The accidental result (16 refusing a 17 datadir, verbatim) is real and kept | 17-postgres-refusal-reproduced.txt |
|
||||
| 21:56 | **B1 — THE UNATTENDED HOLD. MEASURED, and it is the thing `09` Q4 has never had.** | The caller pressed ONCE with nobody watching; the drill image started, stayed up and never served; `safety-dump` -> `verifying` -> **`held after 312.9 s`**; then **pass 2 and pass 3 pressed NOTHING** — `outcomes={'glance': ('held', 312.9)} never_again=['glance']`. The hold sentence names the tier, the date and what the copy holds. **My `follow()` fix (R-623) was load-bearing: without it this would have read `timeout` after 900 s** | bad-days/B1-unattended-hold/ |
|
||||
| 21:57 | **B2 — the new tag cannot be pulled** | **Exactly §6.1 Scenario E.** `phase=failed` in **1.0 s**, `hold=None`, „Az új verzió letöltése nem sikerült, ezért a frissítés elmaradt. Az alkalmazás a korábbi verzióval fut tovább." Nothing held; the app keeps running the old version | bad-days/B2-pull-fails/ |
|
||||
| 21:57 | **B3 — Update pressed while a backup runs** | `409 reason='busy'` on six consecutive presses with „A frissítés most nem indítható: mentés/visszaállítás folyamatban. Próbáld újra, ha befejeződött." — the transient reason an unattended caller needs (R-609) | bad-days/B3-busy-during-backup/ |
|
||||
| 21:58 | **B7 — the 2 GB disk floor** | **REFUSED correctly**: filled to 1.4 GB free, `409 reason='disk'`, „Nincs elég szabad hely a frissítéshez: 1.4 GB szabad… legalább 2 GB szükséges." Nothing moved. Space released immediately after | bad-days/B7-disk-and-memory-refusals/ |
|
||||
| 21:58 | **CORRECTION to R-606** — the refusals are NOT English | Three refusals requested with `?lang=en`, Hungarian request as control: `held`, `not_deployed` and `disk` all come back **identical Hungarian**. The pipe exists (`langFor` honours `?lang=`) but the sentences are frozen string constants, so routing them through `errText` changes nothing | 20-refusals-in-english.txt |
|
||||
| 22:00 | **B9 — a frozen app gets a newer `.felhom.yml`** | §5.4's asymmetry confirmed live; **no false alarm** — 10 samples, all `running`/200. The reason NARROWS R-458: `type: http` treats any response as healthy, so a wrong path is invisible; the risk is real only for `type: api` + `expect` | 21-B9-frozen-app-newer-felhomyml.md |
|
||||
| 22:00 | B8 skipped here — docmost was swept by B1's precondition | re-run in `phase2_redo.sh` with a freshly deployed docmost | phase2_redo.sh |
|
||||
| 22:02 | **B4 (two) — two Updates within one second** | **THERE IS NO SINGLE-FLIGHT.** Both accepted `202` within **0.268 s**, and **2 of 2 ran in flight at the same time**. Both ended `done`, no error, no hold, both pins advanced. That is a real Slice-6 design input: an unattended caller pressing N apps would run N updates at once | bad-days/B4-concurrent-updates/two.json |
|
||||
| 22:06 | **B4 (five) — five Updates within half a second** | **5 of 5 ran concurrently and ALL ended honest**: accepted within **0.448 s**, every one `done`, no error, no hold, every pin advanced, whole batch ~30 s. So the box does not serialise updates at all — and on this box it coped. Memory samples in the record | bad-days/B4-concurrent-updates/five.json |
|
||||
| 22:08 | **B5 (backing-up cut) — the phase nobody had cut in** | **BEHAVED.** Cut at `backing-up` +0.027 s (`pct stop` returned 9.65 s later — the usual instrument limit, stated). After boot the box said so ITSELF: „crash recovery: an app-data backup (volume dump) … was interrupted and left 1 app(s) stopped — restarting them: [privatebin]" and „update recovery: privatebin was interrupted in backing-up". **The pin did NOT move**, the app runs, and a fresh paste seeded+read back | bad-days/B5-backing-up/ |
|
||||
| 22:08 | **B5 (safety-dump cut) — MISSED, recorded as a miss** | The phases went `backing-up` -> `pulling` -> `failed` in **0.473 s** and `safety-dump` was never observed, so the plug was never pulled. Recorded as a MISS, not as a pass. To be retried with a genuine pending edge | bad-days/B5-safety-dump/ |
|
||||
| 22:09 | **Phase 4 — the morning after** | Every one of the **10 badges is TRUE** (refs equal <-> „Naprakész", refs differ <-> „Frissítés elérhető"). Q4's four promises all scored True on the held app's page. `zipline` shows the household „Nem egészséges — URL nem elérhető" while running — R-618 in the household's own words | bad-days/P4-morning-after/ |
|
||||
| 22:09 | **B6 — the way out FORWARDS: REFUSED** | A held app met a FIXED newer version. Badge: „Frissítés elérhető — ma" in both languages, button offered; press -> **`409 reason='held'`**. Correct per §6.1 (only a restore lifts a hold) but **the page invites what the button refuses** — the exact inconsistency R-524 removed for the Ahead case. Filed as **R-625** | bad-days/B6-way-out-forwards/ |
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
21:34:35 ==== FAILWALK adventurelog: the household's way out of a held update
|
||||
21:34:37 [1] held state: phase=failed hold='A(z) adventurelog frissítése 2026-09-21 20:53-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 20:47 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.'
|
||||
21:34:37 update_error = 'A(z) adventurelog frissítése 2026-09-21 20:53-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 20:47 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.'
|
||||
21:34:37 pinned = {'adventurelog': 'ghcr.io/seanmorley15/adventurelog-backend:v0.13.0', 'adventurelog-frontend': 'ghcr.io/seanmorley15/adventurelog-frontend:v0.13.0', 'adventurelog-postgres': 'postgis/postgis:16-3.5-alpine'}
|
||||
21:34:37 installed = {'adventurelog': 'ghcr.io/seanmorley15/adventurelog-backend:v0.12.1', 'adventurelog-frontend': 'ghcr.io/seanmorley15/adventurelog-frontend:v0.12.1', 'adventurelog-postgres': 'postgis/postgis:16-3.5-alpine'}
|
||||
21:34:37 compose = ['image: ghcr.io/seanmorley15/adventurelog-backend:v0.13.0', 'image: postgis/postgis:16-3.5-alpine', 'image: ghcr.io/seanmorley15/adventurelog-frontend:v0.13.0']
|
||||
21:34:37 inspect = []
|
||||
21:34:37 [2] hold sentence HU: ['AdventureLog — Felhom.eu Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Telepített alkalmazás nem fut: AdventureLog (stopped) Rendszermonitor → Telepített alkalmazás nem fut: Tandoor Recipes (stopped) Rendszermonitor → Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások AdventureLog Leállítva Frissítés elérhető — ma Megnyitás ↗ Napló Exportálás Beállítások A(z) adventurelog frissítése 2026-09-21 20:53-kor nem sikerült, és az alkalmazás nem indult el az új verzióval.', 'Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 20:47 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.', 'Mentések Utazási napló - utazások dokumentálása térképpel és fotókkal ~100M RAM travel Pi kompatibilis Áthelyezés másik tárhelyre Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át.', 'Utazások dokumentálása képekkel és jegyzetekkel Térképes megjelenítés a meglátogatott helyekről Úti tervek készítése és szervezése Statisztikák - meglátogatott országok, városok Kalandok megosztása családdal és barátokkal Első lépések Nyisd meg a travel.DOMAIN címet a böngészőben Hozd létre a fiókodat Add hozzá az első utazásodat Jelöld meg a meglátogatott helyeket a térképen Dokumentáció Hivatalos dokumentáció ↗']
|
||||
21:34:37 [2] hold sentence EN: ['AdventureLog — Felhom.eu Launcher Dashboard Apps Storage Drives Network storage Backup Overview Remote backup Apps Restore Sharing Network sharing System monitor Debug Settings System Notifications Security and access 0.261.0 Magyar English Sign out ↗ An installed app is not running: AdventureLog (stopped) System monitor → An installed app is not running: Tandoor Recipes (stopped) System monitor → The hub connection is off — central monitoring is not running System monitor → ← Apps AdventureLog Stopped Update available — today Open ↗ Log Export Settings A(z) adventurelog frissítése 2026-09-21 20:53-kor nem sikerült, és az alkalmazás nem indult el az új verzióval.', 'Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 20:47 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.', 'Backups Travel journal - keep your trips with a map and photos ~100M RAM travel Runs on Pi Move to another storage You can move this app’s data to another connected storage.', 'Keep your trips with pictures and notes See everywhere you have been on a map Plan and organise trips before you go Statistics - the countries and cities you have visited Share your adventures with family and friends First steps Open travel.DOMAIN in your browser Create your account Add your first trip Mark the places you have been on the map Documentation Official documentation ↗']
|
||||
21:34:37 [3] the last migration line the app printed, verbatim: None
|
||||
21:34:37 [R] restoring adventurelog from snapshot 'helyi' (of 1 offered)
|
||||
21:34:38 [R] POST /backup/restore -> HTTP/2 302 ['location: /backups/restore?flash=flash.restore.started']
|
||||
21:34:38 + 0.0s restore (True, None, None)
|
||||
21:35:52 + 74.9s restore (False, None, None)
|
||||
21:35:52 [R] after restore: state=running hold=None phase=None
|
||||
21:35:55 [4] restore: HTTP/2 302 in 75.0s; state=running hold=None
|
||||
21:35:55 pinned after = {'adventurelog': 'ghcr.io/seanmorley15/adventurelog-backend:v0.12.1', 'adventurelog-frontend': 'ghcr.io/seanmorley15/adventurelog-frontend:v0.12.1', 'adventurelog-postgres': 'postgis/postgis:16-3.5-alpine'}
|
||||
21:35:55 inspect after = ['adventurelog-frontend ghcr.io/seanmorley15/adventurelog-frontend:v0.12.1 running=true restarts=0', 'adventurelog ghcr.io/seanmorley15/adventurelog-backend:v0.12.1 running=true restarts=0', 'adventurelog-postgres postgis/postgis:16-3.5-alpine running=true restarts=0']
|
||||
21:36:00 adventurelog: createsuperuser :: Superuser created successfully.
|
||||
21:36:04 adventurelog: seeded superuser drill4471b3
|
||||
21:36:12 adventurelog: readback of the seeded account found=True
|
||||
21:36:12 [5] the app answers its own front door again and holds data: True
|
||||
21:36:12 [6] sentence after the restore HU: ['AdventureLog — Felhom.eu Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Telepített alkalmazás nem fut: Tandoor Recipes (stopped) Rendszermonitor → Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások AdventureLog Fut Frissítés elérhető — 65 napja Megnyitás ↗ Napló Exportálás Beállítások Utazási napló - utazások dokumentálása térképpel és fotókkal ~100M RAM travel Pi kompatibilis Áthelyezés másik tárhelyre Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át.', 'Utazások dokumentálása képekkel és jegyzetekkel Térképes megjelenítés a meglátogatott helyekről Úti tervek készítése és szervezése Statisztikák - meglátogatott országok, városok Kalandok megosztása családdal és barátokkal Első lépések Nyisd meg a travel.DOMAIN címet a böngészőben Hozd létre a fiókodat Add hozzá az első utazásodat Jelöld meg a meglátogatott helyeket a térképen Dokumentáció Hivatalos dokumentáció ↗']
|
||||
@@ -0,0 +1,111 @@
|
||||
{
|
||||
"leg": "failwalk",
|
||||
"app": "adventurelog",
|
||||
"held": {
|
||||
"state": "stopped",
|
||||
"updating": false,
|
||||
"update_phase": "failed",
|
||||
"update_phase_label": "A frissítés nem sikerült",
|
||||
"update_error": "A(z) adventurelog frissítése 2026-09-21 20:53-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 20:47 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"hold_reason": "A(z) adventurelog frissítése 2026-09-21 20:53-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 20:47 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"observables": {
|
||||
"pinned_images": {
|
||||
"adventurelog": "ghcr.io/seanmorley15/adventurelog-backend:v0.13.0",
|
||||
"adventurelog-frontend": "ghcr.io/seanmorley15/adventurelog-frontend:v0.13.0",
|
||||
"adventurelog-postgres": "postgis/postgis:16-3.5-alpine"
|
||||
},
|
||||
"installed_images": {
|
||||
"adventurelog": "ghcr.io/seanmorley15/adventurelog-backend:v0.12.1",
|
||||
"adventurelog-frontend": "ghcr.io/seanmorley15/adventurelog-frontend:v0.12.1",
|
||||
"adventurelog-postgres": "postgis/postgis:16-3.5-alpine"
|
||||
},
|
||||
"catalog_images": {
|
||||
"adventurelog": "ghcr.io/seanmorley15/adventurelog-backend:v0.13.0",
|
||||
"adventurelog-frontend": "ghcr.io/seanmorley15/adventurelog-frontend:v0.13.0",
|
||||
"adventurelog-postgres": "postgis/postgis:16-3.5-alpine"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: ghcr.io/seanmorley15/adventurelog-backend:v0.13.0",
|
||||
"image: postgis/postgis:16-3.5-alpine",
|
||||
"image: ghcr.io/seanmorley15/adventurelog-frontend:v0.13.0"
|
||||
],
|
||||
"docker_inspect": []
|
||||
}
|
||||
},
|
||||
"hold_sentences": {
|
||||
"hu": [
|
||||
"AdventureLog — Felhom.eu Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Telepített alkalmazás nem fut: AdventureLog (stopped) Rendszermonitor → Telepített alkalmazás nem fut: Tandoor Recipes (stopped) Rendszermonitor → Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások AdventureLog Leállítva Frissítés elérhető — ma Megnyitás ↗ Napló Exportálás Beállítások A(z) adventurelog frissítése 2026-09-21 20:53-kor nem sikerült, és az alkalmazás nem indult el az új verzióval.",
|
||||
"Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 20:47 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"Mentések Utazási napló - utazások dokumentálása térképpel és fotókkal ~100M RAM travel Pi kompatibilis Áthelyezés másik tárhelyre Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át.",
|
||||
"Utazások dokumentálása képekkel és jegyzetekkel Térképes megjelenítés a meglátogatott helyekről Úti tervek készítése és szervezése Statisztikák - meglátogatott országok, városok Kalandok megosztása családdal és barátokkal Első lépések Nyisd meg a travel.DOMAIN címet a böngészőben Hozd létre a fiókodat Add hozzá az első utazásodat Jelöld meg a meglátogatott helyeket a térképen Dokumentáció Hivatalos dokumentáció ↗"
|
||||
],
|
||||
"en": [
|
||||
"AdventureLog — Felhom.eu Launcher Dashboard Apps Storage Drives Network storage Backup Overview Remote backup Apps Restore Sharing Network sharing System monitor Debug Settings System Notifications Security and access 0.261.0 Magyar English Sign out ↗ An installed app is not running: AdventureLog (stopped) System monitor → An installed app is not running: Tandoor Recipes (stopped) System monitor → The hub connection is off — central monitoring is not running System monitor → ← Apps AdventureLog Stopped Update available — today Open ↗ Log Export Settings A(z) adventurelog frissítése 2026-09-21 20:53-kor nem sikerült, és az alkalmazás nem indult el az új verzióval.",
|
||||
"Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 20:47 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"Backups Travel journal - keep your trips with a map and photos ~100M RAM travel Runs on Pi Move to another storage You can move this app’s data to another connected storage.",
|
||||
"Keep your trips with pictures and notes See everywhere you have been on a map Plan and organise trips before you go Statistics - the countries and cities you have visited Share your adventures with family and friends First steps Open travel.DOMAIN in your browser Create your account Add your first trip Mark the places you have been on the map Documentation Official documentation ↗"
|
||||
]
|
||||
},
|
||||
"last_migration_line": null,
|
||||
"way_out": {
|
||||
"ok": true,
|
||||
"snapshot_id": "helyi",
|
||||
"snapshots": [
|
||||
{
|
||||
"time": "2026-09-21T18:48:09Z",
|
||||
"short_id": "helyi",
|
||||
"tier": 1,
|
||||
"drive_label": "Belső SSD (rendszer)"
|
||||
}
|
||||
],
|
||||
"http": "HTTP/2 302",
|
||||
"location": [
|
||||
"location: /backups/restore?flash=flash.restore.started"
|
||||
],
|
||||
"seconds": 75.0,
|
||||
"state_after": "running",
|
||||
"hold_after": null,
|
||||
"observables_after": {
|
||||
"pinned_images": {
|
||||
"adventurelog": "ghcr.io/seanmorley15/adventurelog-backend:v0.12.1",
|
||||
"adventurelog-frontend": "ghcr.io/seanmorley15/adventurelog-frontend:v0.12.1",
|
||||
"adventurelog-postgres": "postgis/postgis:16-3.5-alpine"
|
||||
},
|
||||
"installed_images": {
|
||||
"adventurelog": "ghcr.io/seanmorley15/adventurelog-backend:v0.12.1",
|
||||
"adventurelog-frontend": "ghcr.io/seanmorley15/adventurelog-frontend:v0.12.1",
|
||||
"adventurelog-postgres": "postgis/postgis:16-3.5-alpine"
|
||||
},
|
||||
"catalog_images": {
|
||||
"adventurelog": "ghcr.io/seanmorley15/adventurelog-backend:v0.13.0",
|
||||
"adventurelog-frontend": "ghcr.io/seanmorley15/adventurelog-frontend:v0.13.0",
|
||||
"adventurelog-postgres": "postgis/postgis:16-3.5-alpine"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: ghcr.io/seanmorley15/adventurelog-backend:v0.12.1",
|
||||
"image: postgis/postgis:16-3.5-alpine",
|
||||
"image: ghcr.io/seanmorley15/adventurelog-frontend:v0.12.1"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"adventurelog-frontend ghcr.io/seanmorley15/adventurelog-frontend:v0.12.1 running=true restarts=0",
|
||||
"adventurelog ghcr.io/seanmorley15/adventurelog-backend:v0.12.1 running=true restarts=0",
|
||||
"adventurelog-postgres postgis/postgis:16-3.5-alpine running=true restarts=0"
|
||||
]
|
||||
}
|
||||
},
|
||||
"data_after_restore": {
|
||||
"route": "re-seeded after the restore",
|
||||
"ok": true,
|
||||
"note": "the pre-update seed token did not survive the failed run's process, so this re-seeds AFTER the restore: it proves the app WORKS again on the old version, not that the specific pre-update row survived. Stated rather than glossed."
|
||||
},
|
||||
"sentences_after_restore": {
|
||||
"hu": [
|
||||
"AdventureLog — Felhom.eu Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Telepített alkalmazás nem fut: Tandoor Recipes (stopped) Rendszermonitor → Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások AdventureLog Fut Frissítés elérhető — 65 napja Megnyitás ↗ Napló Exportálás Beállítások Utazási napló - utazások dokumentálása térképpel és fotókkal ~100M RAM travel Pi kompatibilis Áthelyezés másik tárhelyre Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át.",
|
||||
"Utazások dokumentálása képekkel és jegyzetekkel Térképes megjelenítés a meglátogatott helyekről Úti tervek készítése és szervezése Statisztikák - meglátogatott országok, városok Kalandok megosztása családdal és barátokkal Első lépések Nyisd meg a travel.DOMAIN címet a böngészőben Hozd létre a fiókodat Add hozzá az első utazásodat Jelöld meg a meglátogatott helyeket a térképen Dokumentáció Hivatalos dokumentáció ↗"
|
||||
],
|
||||
"en": [
|
||||
"AdventureLog — Felhom.eu Launcher Dashboard Apps Storage Drives Network storage Backup Overview Remote backup Apps Restore Sharing Network sharing System monitor Debug Settings System Notifications Security and access 0.261.0 Magyar English Sign out ↗ An installed app is not running: Tandoor Recipes (stopped) System monitor → The hub connection is off — central monitoring is not running System monitor → ← Apps AdventureLog Running Update available — 65 days ago Open ↗ Log Export Settings Travel journal - keep your trips with a map and photos ~100M RAM travel Runs on Pi Move to another storage You can move this app’s data to another connected storage.",
|
||||
"Keep your trips with pictures and notes See everywhere you have been on a map Plan and organise trips before you go Statistics - the countries and cities you have visited Share your adventures with family and friends First steps Open travel.DOMAIN in your browser Create your account Add your first trip Mark the places you have been on the map Documentation Official documentation ↗"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,662 @@
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="no-js">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AdventureLog — Felhom.eu</title>
|
||||
|
||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg?v=0.261.0">
|
||||
<link rel="stylesheet" href="/static/style.css?v=0.261.0">
|
||||
<meta name="csrf-token" content="f1b38af3981c5aa0730617a1677adcfa060cef61805c532aa5f440be5feef25a">
|
||||
<script>
|
||||
|
||||
|
||||
document.documentElement.className=document.documentElement.className.replace('no-js','js');
|
||||
function csrfHeaders(){var el=document.querySelector('meta[name="csrf-token"]');return el?{'X-CSRF-Token':el.content}:{};}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function felhomConfirm(el,question,onYes){
|
||||
if(!el||el.dataset.fcOpen)return;
|
||||
el.dataset.fcOpen='1';
|
||||
var wrap=document.createElement('span');wrap.className='inline-confirm';
|
||||
var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question;
|
||||
var yes=document.createElement('button');yes.type='button';yes.className='btn btn-xs btn-danger';yes.textContent='Yes';
|
||||
var no=document.createElement('button');no.type='button';no.className='btn btn-xs btn-outline';no.textContent='Cancel';
|
||||
wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no);
|
||||
el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling);
|
||||
function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;}
|
||||
no.addEventListener('click',close);
|
||||
yes.addEventListener('click',function(){close();onYes();});
|
||||
}
|
||||
document.addEventListener('click',function(e){
|
||||
var btn=e.target.closest?e.target.closest('[data-confirm]'):null;
|
||||
if(!btn)return;
|
||||
e.preventDefault();
|
||||
felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){
|
||||
var form=btn.closest('form');
|
||||
if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
document.addEventListener('click',function(e){
|
||||
var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null;
|
||||
if(!btn)return;
|
||||
var group=btn.closest('.nav-group');
|
||||
if(!group)return;
|
||||
var willOpen=!group.classList.contains('is-open');
|
||||
document.querySelectorAll('.nav-group.is-open').forEach(function(g){
|
||||
g.classList.remove('is-open');
|
||||
var t=g.querySelector('.nav-group-toggle');
|
||||
if(t)t.setAttribute('aria-expanded','false');
|
||||
});
|
||||
if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');}
|
||||
});
|
||||
function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML='<h3>Message</h3><pre style="white-space:pre-wrap;word-break:break-word;background:var(--bg-2);padding:.75rem;border-radius:.375rem;font-size:.85rem;max-height:60vh;overflow-y:auto;user-select:text;cursor:text">'+msg.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')+'</pre><div class="modal-actions"><button class="btn btn-primary" onclick="document.getElementById(\'alert-modal\').remove()">OK</button></div>';o.appendChild(c);document.body.appendChild(o);}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display:none" aria-hidden="true">
|
||||
<symbol id="i-hard-drive" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 16h.01" /> <path d="M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" /> <path d="M21.946 12.013H2.054" /> <path d="M6 16h.01" /></symbol>
|
||||
<symbol id="i-cpu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20v2" /> <path d="M12 2v2" /> <path d="M17 20v2" /> <path d="M17 2v2" /> <path d="M2 12h2" /> <path d="M2 17h2" /> <path d="M2 7h2" /> <path d="M20 12h2" /> <path d="M20 17h2" /> <path d="M20 7h2" /> <path d="M7 20v2" /> <path d="M7 2v2" /> <rect x="4" y="4" width="16" height="16" rx="2" /> <rect x="8" y="8" width="8" height="8" rx="1" /></symbol>
|
||||
<symbol id="i-memory-stick" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 12v-2" /> <path d="M12 18v-2" /> <path d="M16 12v-2" /> <path d="M16 18v-2" /> <path d="M2 11h1.5" /> <path d="M20 18v-2" /> <path d="M20.5 11H22" /> <path d="M4 18v-2" /> <path d="M8 12v-2" /> <path d="M8 18v-2" /> <rect x="2" y="6" width="20" height="10" rx="2" /></symbol>
|
||||
<symbol id="i-thermometer" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 4v10.54a4 4 0 1 1-4 0V4a2 2 0 0 1 4 0Z" /></symbol>
|
||||
<symbol id="i-triangle-alert" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" /> <path d="M12 9v4" /> <path d="M12 17h.01" /></symbol>
|
||||
<symbol id="i-lock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="11" x="3" y="11" rx="2" ry="2" /> <path d="M7 11V7a5 5 0 0 1 10 0v4" /></symbol>
|
||||
<symbol id="i-cloud" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z" /></symbol>
|
||||
<symbol id="i-square" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" /></symbol>
|
||||
<symbol id="i-rotate-cw" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" /> <path d="M21 3v5h-5" /></symbol>
|
||||
<symbol id="i-file-text" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /> <path d="M14 2v5a1 1 0 0 0 1 1h5" /> <path d="M10 9H8" /> <path d="M16 13H8" /> <path d="M16 17H8" /></symbol>
|
||||
<symbol id="i-external-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6" /> <path d="M10 14 21 3" /> <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></symbol>
|
||||
<symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /></symbol>
|
||||
<symbol id="i-server" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="8" x="2" y="2" rx="2" ry="2" /> <rect width="20" height="8" x="2" y="14" rx="2" ry="2" /> <line x1="6" x2="6.01" y1="6" y2="6" /> <line x1="6" x2="6.01" y1="18" y2="18" /></symbol>
|
||||
<symbol id="i-share" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3" /> <circle cx="6" cy="12" r="3" /> <circle cx="18" cy="19" r="3" /> <line x1="8.59" x2="15.42" y1="13.51" y2="17.49" /> <line x1="15.41" x2="8.59" y1="6.51" y2="10.49" /></symbol>
|
||||
<symbol id="i-layout-grid" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="7" x="3" y="3" rx="1" /> <rect width="7" height="7" x="14" y="3" rx="1" /> <rect width="7" height="7" x="14" y="14" rx="1" /> <rect width="7" height="7" x="3" y="14" rx="1" /></symbol>
|
||||
<symbol id="i-rocket" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91 0z" /> <path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z" /> <path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0" /> <path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5" /></symbol>
|
||||
<symbol id="i-settings" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /> <circle cx="12" cy="12" r="3" /></symbol>
|
||||
<symbol id="i-bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.268 21a2 2 0 0 0 3.464 0" /> <path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" /></symbol>
|
||||
<symbol id="i-x" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18" /> <path d="m6 6 12 12" /></symbol>
|
||||
<symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5" /></symbol>
|
||||
<symbol id="i-pencil" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" /> <path d="m15 5 4 4" /></symbol>
|
||||
<symbol id="i-info" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /> <path d="M12 16v-4" /> <path d="M12 8h.01" /></symbol>
|
||||
<symbol id="i-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 15V3" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /> <path d="m7 10 5 5 5-5" /></symbol>
|
||||
<symbol id="i-upload" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v12" /> <path d="m17 8-5-5-5 5" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /></symbol>
|
||||
<symbol id="i-trash-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 11v6" /> <path d="M14 11v6" /> <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /> <path d="M3 6h18" /> <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></symbol>
|
||||
<symbol id="i-wrench" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z" /></symbol>
|
||||
<symbol id="i-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" /> <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" /></symbol>
|
||||
<symbol id="i-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34" /> <circle cx="11" cy="11" r="8" /></symbol>
|
||||
<symbol id="i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14" /> <path d="M12 5v14" /></symbol>
|
||||
<symbol id="i-chevron-down" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6" /></symbol>
|
||||
<symbol id="i-play" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z" /></symbol>
|
||||
<symbol id="i-pause" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="14" y="3" width="5" height="18" rx="1" /> <rect x="5" y="3" width="5" height="18" rx="1" /></symbol>
|
||||
<symbol id="i-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h16" /> <path d="M4 6h16" /> <path d="M4 18h16" /></symbol>
|
||||
</svg>
|
||||
<header class="mobile-topbar">
|
||||
<a href="/" class="mobile-topbar-logo"><img src="/static/felhom-logo.svg?v=0.261.0" alt="Felhom.eu"></a>
|
||||
<button type="button" class="nav-burger" aria-expanded="false" aria-controls="sidebar" aria-label="Menu">
|
||||
<svg class="ico"><use href="#i-menu"/></svg>
|
||||
</button>
|
||||
</header>
|
||||
<div class="nav-backdrop" hidden></div>
|
||||
<nav class="sidebar" id="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<img src="/static/felhom-logo.svg?v=0.261.0" alt="Felhom.eu" class="sidebar-logo">
|
||||
</div>
|
||||
<ul class="nav-links">
|
||||
<li><a href="/launcher" class=""><svg class="ico"><use href="#i-rocket"/></svg>Launcher</a></li>
|
||||
<li><a href="/dashboard" class=""><svg class="ico"><use href="#i-layout-grid"/></svg>Dashboard</a></li>
|
||||
<li><a href="/stacks" class="active"><svg class="ico"><use href="#i-cloud"/></svg>Apps</a></li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-storage"><svg class="ico"><use href="#i-hard-drive"/></svg>Storage<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-storage" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/storage" class="">Drives</a></li>
|
||||
<li><a href="/storage/network" class="">Network storage</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-backups"><svg class="ico"><use href="#i-shield"/></svg>Backup<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-backups" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/backups" class="">Overview</a></li>
|
||||
<li><a href="/backups/remote" class="">Remote backup</a></li>
|
||||
<li><a href="/backups/apps" class="">Apps</a></li>
|
||||
<li><a href="/backups/restore" class="">Restore</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-sharing"><svg class="ico"><use href="#i-share"/></svg>Sharing<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-sharing" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/sharing" class="">Network sharing</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/monitoring" class=""><svg class="ico"><use href="#i-cpu"/></svg>System monitor</a></li>
|
||||
<li><a href="/debug" class=""><svg class="ico"><use href="#i-wrench"/></svg>Debug</a></li>
|
||||
</ul>
|
||||
<div class="sidebar-bottom">
|
||||
<div class="nav-group-label">Settings</div>
|
||||
<ul class="nav-links nav-links-sub">
|
||||
<li><a href="/settings" class=""><svg class="ico"><use href="#i-settings"/></svg>System</a></li>
|
||||
<li><a href="/settings/notifications" class=""><svg class="ico"><use href="#i-bell"/></svg>Notifications</a></li>
|
||||
<li><a href="/settings/security" class=""><svg class="ico"><use href="#i-lock"/></svg>Security and access</a></li>
|
||||
</ul>
|
||||
<div class="sidebar-footer">
|
||||
<span class="version">0.261.0</span><details class="lang-globe">
|
||||
<summary class="lang-globe-btn" aria-label="Language" title="Language"><svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10" /><path d="M2 12h20" /><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" /></svg></summary>
|
||||
<ul class="lang-globe-menu">
|
||||
<li><form method="POST" action="/settings/language"><input type="hidden" name="_csrf" value="f1b38af3981c5aa0730617a1677adcfa060cef61805c532aa5f440be5feef25a"><input type="hidden" name="back" value="/apps/adventurelog"><button type="submit" name="lang" value="hu" lang="hu" class="lang-globe-item">Magyar</button></form></li>
|
||||
<li><form method="POST" action="/settings/language"><input type="hidden" name="_csrf" value="f1b38af3981c5aa0730617a1677adcfa060cef61805c532aa5f440be5feef25a"><input type="hidden" name="back" value="/apps/adventurelog"><button type="submit" name="lang" value="en" lang="en" class="lang-globe-item current" aria-current="true">English</button></form></li>
|
||||
</ul>
|
||||
</details>
|
||||
<a href="/logout" class="logout-link">Sign out ↗</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<main class="content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="alerts-container">
|
||||
|
||||
|
||||
<div class="alert-banner alert-banner-warning">
|
||||
<span class="alert-icon"><svg class="ico"><use href="#i-triangle-alert"/></svg></span>
|
||||
<span class="alert-message">An installed app is not running: Tandoor Recipes (stopped)</span>
|
||||
<a href="/monitoring" class="alert-link">System monitor →</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="alert-banner alert-banner-warning">
|
||||
<span class="alert-icon"><svg class="ico"><use href="#i-triangle-alert"/></svg></span>
|
||||
<span class="alert-message">The hub connection is off — central monitoring is not running</span>
|
||||
<a href="/monitoring" class="alert-link">System monitor →</a>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="page-header">
|
||||
<div style="display:flex;align-items:center;gap:1rem">
|
||||
<a href="/stacks" class="btn btn-sm btn-outline">← Apps</a>
|
||||
<h2>AdventureLog</h2>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:.5rem">
|
||||
|
||||
<span class="stack-state-badge state-run">Running</span>
|
||||
|
||||
|
||||
<span class="tag tag-warn" title="A newer version of this app is available. Select the Update button to start it.">Update available — 65 days ago</span>
|
||||
<a href="https://travel.enkisfelhom.hu" target="_blank" class="btn btn-sm btn-outline">Open ↗</a>
|
||||
<a href="/stacks/adventurelog/logs" class="btn btn-sm btn-outline">Log</a>
|
||||
|
||||
<a href="/stacks/adventurelog/export" class="btn btn-sm btn-outline">Export</a>
|
||||
<a href="/stacks/adventurelog/deploy" class="btn btn-sm btn-outline">Settings</a>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-hero">
|
||||
<img class="app-info-logo" src="/static/assets/adventurelog-logo.svg"
|
||||
alt="AdventureLog" data-fallback="/static/app-placeholder.svg"
|
||||
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/adventurelog-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
||||
<div class="app-info-hero-text">
|
||||
|
||||
<p class="app-info-tagline">Travel journal - keep your trips with a map and photos</p>
|
||||
|
||||
<div class="stack-meta-badges">
|
||||
<span class="meta-badge">~100M RAM</span>
|
||||
<span class="meta-badge">travel</span>
|
||||
|
||||
<span class="meta-badge meta-badge-ok">Runs on Pi</span>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="app-screenshots" id="screenshots">
|
||||
<img src="/static/assets/adventurelog-screenshot-1.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
<img src="/static/assets/adventurelog-screenshot-2.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
<img src="/static/assets/adventurelog-screenshot-3.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card" style="margin-top:1rem">
|
||||
<h3>Move to another storage</h3>
|
||||
<p class="form-hint">You can move this app’s data to another connected storage. The app stops briefly during the move, and the data is deleted from the old place only after the check and a successful restart.</p>
|
||||
<div style="display:flex;gap:.5rem;align-items:center;flex-wrap:wrap;margin-top:.5rem">
|
||||
<select id="app-migrate-target" class="btn btn-sm btn-outline">
|
||||
<option value="">Choose a target storage…</option>
|
||||
<option value="/mnt/felhom-drives/scratch_hdd">SCRATCH HDD (/mnt/felhom-drives/scratch_hdd)</option>
|
||||
</select>
|
||||
<button class="btn btn-sm btn-outline" onclick="appMigrate(this,'adventurelog','AdventureLog')">Move</button>
|
||||
</div>
|
||||
<div id="app-migrate-progress" style="display:none;margin-top:.75rem;padding:.75rem;border:1px solid var(--blue);border-radius:6px;background:rgba(0,136,204,0.06)"></div>
|
||||
</div>
|
||||
<script>
|
||||
function appMigFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; }
|
||||
function appMigRender(job){
|
||||
var names={stop:'Stopping',copy:'Copying data',verify:'Checking',flip:'Reinstalling',redeploy:'Reinstalling',cleanup:'Deleting old data'};
|
||||
var s=names[job.phase]||job.phase;
|
||||
if(job.phase==='copy'&&job.bytes_total>0){ s+=' ('+Math.floor(100*job.bytes_done/job.bytes_total)+'% — '+appMigFmtGB(job.bytes_done)+'/'+appMigFmtGB(job.bytes_total)+')'; }
|
||||
return s+'…';
|
||||
}
|
||||
function appMigWatch(){
|
||||
var panel=document.getElementById('app-migrate-progress');
|
||||
if(panel) panel.style.display='block';
|
||||
function tick(){
|
||||
fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){
|
||||
var job=d.data&&d.data.job;
|
||||
if(!job){ if(panel) panel.textContent='No move in progress.'; return; }
|
||||
if(panel) panel.innerHTML=appMigRender(job);
|
||||
if(job.phase==='done'){ if(panel) panel.innerHTML+='<br><strong>Done</strong>'; setTimeout(function(){location.reload();},1500); return; }
|
||||
if(job.phase==='aborted'){ if(panel) panel.innerHTML+='<br><strong style="color:var(--danger,#c0392b)">Stopped: '+(job.error||'')+'</strong><br>The old data is untouched.'; return; }
|
||||
setTimeout(tick,1500);
|
||||
}).catch(function(){ setTimeout(tick,2000); });
|
||||
}
|
||||
tick();
|
||||
}
|
||||
function appMigrate(btn,app,label){
|
||||
var sel=document.getElementById('app-migrate-target');
|
||||
var target=sel?sel.value:'';
|
||||
if(!target){ showAlert('Choose a target storage.'); return; }
|
||||
felhomConfirm(btn,'Move the '+label+' data to: '+target+'? The app stops briefly. The old data is deleted only after a successful move.',function(){
|
||||
fetch('/api/storage/migrate-app',{method:'POST',headers:Object.assign({'Content-Type':'application/json'},csrfHeaders()),body:JSON.stringify({app:app,target:target})})
|
||||
.then(function(r){return r.json();}).then(function(d){ if(d.ok){ appMigWatch(); } else { showAlert('Error: '+(d.error||'unknown')); } })
|
||||
.catch(function(e){ showAlert('Error: '+e); });
|
||||
});
|
||||
}
|
||||
(function(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ if(d.data&&d.data.job){ appMigWatch(); } }).catch(function(){}); })();
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<div class="app-info-grid">
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>What is it for?</h3>
|
||||
<ul class="app-info-list">
|
||||
<li>Keep your trips with pictures and notes</li><li>See everywhere you have been on a map</li><li>Plan and organise trips before you go</li><li>Statistics - the countries and cities you have visited</li><li>Share your adventures with family and friends</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>First steps</h3>
|
||||
<ol class="app-info-list">
|
||||
<li>Open travel.DOMAIN in your browser</li><li>Create your account</li><li>Add your first trip</li><li>Mark the places you have been on the map</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>Documentation</h3>
|
||||
<p><a href="https://adventurelog.app/docs/" target="_blank" class="app-info-link">Official documentation ↗</a></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
(function(){
|
||||
var burger=document.querySelector('.nav-burger');
|
||||
var sidebar=document.getElementById('sidebar');
|
||||
var backdrop=document.querySelector('.nav-backdrop');
|
||||
if(!burger||!sidebar||!backdrop)return;
|
||||
function setOpen(open){
|
||||
sidebar.classList.toggle('is-open',open);
|
||||
document.body.classList.toggle('nav-open',open);
|
||||
backdrop.hidden=!open;
|
||||
burger.setAttribute('aria-expanded',open?'true':'false');
|
||||
}
|
||||
burger.addEventListener('click',function(){setOpen(!sidebar.classList.contains('is-open'));});
|
||||
backdrop.addEventListener('click',function(){setOpen(false);});
|
||||
document.addEventListener('keydown',function(e){
|
||||
if(e.key==='Escape'&&sidebar.classList.contains('is-open'))setOpen(false);
|
||||
});
|
||||
})();
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.closest('a, button, .btn, input, select, textarea, .app-row-actions, .stack-detail-actions')) return;
|
||||
var card = e.target.closest('[data-href]');
|
||||
if (card) window.location.href = card.dataset.href;
|
||||
});
|
||||
async function checkBeforeDeploy(e, name) {
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name);
|
||||
var data = await resp.json();
|
||||
if (data.ok && data.data && data.data.deployed) {
|
||||
e.preventDefault();
|
||||
showAlert('This app is already installed.');
|
||||
window.location.reload();
|
||||
return false;
|
||||
}
|
||||
} catch(err) {}
|
||||
return true;
|
||||
}
|
||||
async function syncTemplates() {
|
||||
const btn = document.getElementById('sync-btn');
|
||||
const toast = document.getElementById('sync-toast');
|
||||
if (!btn) return;
|
||||
const origText = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '↻ Refreshing...';
|
||||
btn.classList.add('loading');
|
||||
try {
|
||||
const resp = await fetch('/api/sync', {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders())
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (toast) {
|
||||
toast.textContent = data.ok ? (data.message || 'Templates refreshed') : ('Error: ' + (data.error || 'Unknown error'));
|
||||
toast.className = 'sync-toast ' + (data.ok ? 'sync-toast-ok' : 'sync-toast-err');
|
||||
toast.style.display = 'block';
|
||||
setTimeout(function() { toast.style.display = 'none'; }, 5000);
|
||||
}
|
||||
if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) {
|
||||
setTimeout(function() { window.location.reload(); }, 1500);
|
||||
}
|
||||
} catch (err) {
|
||||
if (toast) {
|
||||
toast.textContent = 'Network error: ' + err.message;
|
||||
toast.className = 'sync-toast sync-toast-err';
|
||||
toast.style.display = 'block';
|
||||
setTimeout(function() { toast.style.display = 'none'; }, 5000);
|
||||
}
|
||||
}
|
||||
btn.innerHTML = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
}
|
||||
async function stackAction(event, name, action) {
|
||||
const btn = event.currentTarget;
|
||||
const origText = btn.textContent;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'In progress...';
|
||||
btn.classList.add('loading');
|
||||
try {
|
||||
const resp = await fetch('/api/stacks/' + name + '/' + action, {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders())
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (!data.ok) {
|
||||
showAlert('Error: ' + (data.error || 'Unknown error'));
|
||||
btn.textContent = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (action === 'update') {
|
||||
followUpdate(name, btn);
|
||||
return;
|
||||
}
|
||||
window.location.reload();
|
||||
} catch (err) {
|
||||
showAlert('Network error: ' + err.message);
|
||||
btn.textContent = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
}
|
||||
}
|
||||
function followUpdate(name, btn) {
|
||||
var started = Date.now();
|
||||
var timer = setInterval(async function() {
|
||||
if (Date.now() - started > 30 * 60 * 1000) { clearInterval(timer); window.location.reload(); return; }
|
||||
try {
|
||||
var r = await fetch('/api/stacks/' + name);
|
||||
var d = await r.json();
|
||||
if (!d.ok || !d.data) return;
|
||||
if (d.data.update_phase_label) btn.textContent = d.data.update_phase_label;
|
||||
if (!d.data.updating) { clearInterval(timer); window.location.reload(); }
|
||||
} catch (e) {}
|
||||
}, 3000);
|
||||
}
|
||||
async function deleteOrphanStack(name) {
|
||||
var modal = document.createElement('div');
|
||||
modal.className = 'modal-overlay';
|
||||
modal.id = 'delete-modal';
|
||||
modal.innerHTML = '<div class="modal-card"><h3>Loading...</h3></div>';
|
||||
modal.addEventListener('click', function(e) { if (e.target === modal) closeDeleteModal(); });
|
||||
document.body.appendChild(modal);
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name + '/hdd-data');
|
||||
var data = await resp.json();
|
||||
var hddInfo = '';
|
||||
var checkboxHTML = '';
|
||||
if (data.ok && data.data && data.data.has_hdd_data) {
|
||||
hddInfo = '<div class="modal-hdd-info"><strong>Your data on the hard drive:</strong>';
|
||||
data.data.hdd_paths.forEach(function(p) {
|
||||
hddInfo += '<div class="modal-hdd-path">' + p.path + ' (' + (p.exists ? p.size_human : 'does not exist') + ')</div>';
|
||||
});
|
||||
hddInfo += '</div>';
|
||||
checkboxHTML = '<label class="modal-checkbox"><input type="checkbox" id="delete-hdd-check"> Delete your data from the hard drive</label>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Delete app: ' + name + '</h3>' +
|
||||
'<p style="color:var(--text-2);font-size:.9rem;margin-bottom:.75rem">This removes the containers, the volumes and the configuration files.</p>' +
|
||||
'<div class="alert alert-warning" style="margin-bottom:.75rem">You cannot undo this!</div>' +
|
||||
hddInfo + checkboxHTML +
|
||||
'<div class="modal-actions">' +
|
||||
'<button class="btn btn-outline" onclick="closeDeleteModal()">Cancel</button>' +
|
||||
'<button class="btn btn-danger" id="confirm-delete-btn" onclick="confirmDelete(\'' + name + '\')">Delete</button>' +
|
||||
'</div>';
|
||||
} catch (err) {
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Error</h3><p style="color:var(--text-2)">Could not load the details: ' + err.message + '</p>' +
|
||||
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeDeleteModal()">Close</button></div>';
|
||||
}
|
||||
}
|
||||
function closeDeleteModal() {
|
||||
var modal = document.getElementById('delete-modal');
|
||||
if (modal) modal.remove();
|
||||
}
|
||||
async function confirmDelete(name) {
|
||||
var btn = document.getElementById('confirm-delete-btn');
|
||||
var checkbox = document.getElementById('delete-hdd-check');
|
||||
var removeHDD = checkbox ? checkbox.checked : false;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Deleting...';
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name, {
|
||||
method: 'DELETE',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({remove_hdd_data: removeHDD})
|
||||
});
|
||||
var data = await resp.json();
|
||||
if (data.ok) {
|
||||
var modal = document.getElementById('delete-modal');
|
||||
var removedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) {
|
||||
removedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Deleted data: ' + data.data.hdd_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
var preservedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) {
|
||||
preservedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Kept data: ' + data.data.hdd_paths_preserved.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.hdd_note) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">' + data.data.hdd_note + '</p>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Deleted!</h3>' +
|
||||
'<p style="color:var(--text-2)">The app (' + name + ') was deleted.</p>' +
|
||||
removedInfo + preservedInfo +
|
||||
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Close</button></div>';
|
||||
} else {
|
||||
showAlert('Error: ' + (data.error || 'Unknown error'));
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Delete';
|
||||
}
|
||||
} catch (err) {
|
||||
showAlert('Network error: ' + err.message);
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Delete';
|
||||
}
|
||||
}
|
||||
async function removeStack(name) {
|
||||
var modal = document.createElement('div');
|
||||
modal.className = 'modal-overlay';
|
||||
modal.id = 'remove-modal';
|
||||
modal.innerHTML = '<div class="modal-card"><h3>Loading...</h3></div>';
|
||||
modal.addEventListener('click', function(e) { if (e.target === modal) closeRemoveModal(); });
|
||||
document.body.appendChild(modal);
|
||||
try {
|
||||
var [hddResp, backupResp] = await Promise.all([
|
||||
fetch('/api/stacks/' + name + '/hdd-data').then(function(r) { return r.json(); }),
|
||||
fetch('/api/stacks/' + name + '/backup-data').then(function(r) { return r.json(); })
|
||||
]);
|
||||
var sections = '';
|
||||
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Always deleted:</strong>' +
|
||||
'<ul style="margin:.25rem 0;padding-left:1.2rem;color:var(--text-2);font-size:.85rem">' +
|
||||
'<li>Docker volumes (database, app configuration)</li>' +
|
||||
'<li>Install configuration (app.yaml)</li>' +
|
||||
'<li>Secondary backup schedule</li>' +
|
||||
'</ul></div>';
|
||||
|
||||
var hddCheckbox = '';
|
||||
if (hddResp.ok && hddResp.data && hddResp.data.has_hdd_data) {
|
||||
var hddPaths = '';
|
||||
hddResp.data.hdd_paths.forEach(function(p) {
|
||||
hddPaths += '<div class="modal-hdd-path">' + p.path + ' (' + (p.exists ? p.size_human : 'does not exist') + ')</div>';
|
||||
});
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Your data on the hard drive:</strong>' + hddPaths +
|
||||
'<label class="modal-checkbox"><input type="checkbox" id="remove-hdd-check"> Delete your data</label>' +
|
||||
'<div class="alert alert-info" style="margin-top:.5rem;font-size:.8rem" id="remove-hdd-keep-warning">' +
|
||||
'If you reinstall the app, you need to import the data again, because the database is deleted. Data you keep is NOT backed up automatically any more.' +
|
||||
'</div></div>';
|
||||
hddCheckbox = '\x3Cscript>document.getElementById("remove-hdd-check").addEventListener("change",function(){document.getElementById("remove-hdd-keep-warning").style.display=this.checked?"none":"";});<\/script>';
|
||||
}
|
||||
|
||||
var backupCheckbox = '';
|
||||
if (backupResp.ok && backupResp.data && backupResp.data.has_backups) {
|
||||
var bkPaths = '';
|
||||
backupResp.data.backup_paths.forEach(function(p) {
|
||||
if (p.exists) bkPaths += '<div class="modal-hdd-path">' + p.path + ' (' + p.size_human + ')</div>';
|
||||
});
|
||||
if (bkPaths) {
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Backup data:</strong>' + bkPaths +
|
||||
'<label class="modal-checkbox"><input type="checkbox" id="remove-backup-check"> Delete backup data</label>' +
|
||||
'<div class="alert alert-info" style="margin-top:.5rem;font-size:.8rem">' +
|
||||
'The nightly restic snapshots cannot be deleted one by one — they expire automatically under the retention rules.' +
|
||||
'</div></div>';
|
||||
}
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Remove app: ' + name + '</h3>' +
|
||||
'<p style="color:var(--text-2);font-size:.9rem;margin-bottom:.75rem">The app goes back to "Not installed". The template stays, and you can install it again.</p>' +
|
||||
'<div class="alert alert-warning" style="margin-bottom:.75rem">You cannot undo this!</div>' +
|
||||
sections +
|
||||
'<div class="modal-actions">' +
|
||||
'<button class="btn btn-outline" onclick="closeRemoveModal()">Cancel</button>' +
|
||||
'<button class="btn btn-danger" id="confirm-remove-btn" onclick="confirmRemoveStack(\'' + name + '\')">Remove</button>' +
|
||||
'</div>' + hddCheckbox;
|
||||
} catch (err) {
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Error</h3><p style="color:var(--text-2)">Could not load the details: ' + err.message + '</p>' +
|
||||
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeRemoveModal()">Close</button></div>';
|
||||
}
|
||||
}
|
||||
function closeRemoveModal() {
|
||||
var modal = document.getElementById('remove-modal');
|
||||
if (modal) modal.remove();
|
||||
}
|
||||
async function confirmRemoveStack(name) {
|
||||
var btn = document.getElementById('confirm-remove-btn');
|
||||
var hddCheck = document.getElementById('remove-hdd-check');
|
||||
var backupCheck = document.getElementById('remove-backup-check');
|
||||
var removeHDD = hddCheck ? hddCheck.checked : false;
|
||||
var removeBackups = backupCheck ? backupCheck.checked : false;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Removing...';
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name + '/remove', {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({remove_hdd_data: removeHDD, remove_backups: removeBackups})
|
||||
});
|
||||
var data = await resp.json();
|
||||
if (data.ok) {
|
||||
var modal = document.getElementById('remove-modal');
|
||||
var removedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) {
|
||||
removedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Deleted data: ' + data.data.hdd_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.backup_paths_removed && data.data.backup_paths_removed.length > 0) {
|
||||
removedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Deleted backups: ' + data.data.backup_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
var preservedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) {
|
||||
preservedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Kept data: ' + data.data.hdd_paths_preserved.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.hdd_note) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">' + data.data.hdd_note + '</p>';
|
||||
}
|
||||
if (data.data && data.data.backup_paths_refused && data.data.backup_paths_refused.length > 0) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Backups not deleted: ' + data.data.backup_paths_refused.join('; ') + '</p>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Removed!</h3>' +
|
||||
'<p style="color:var(--text-2)">The app (' + name + ') was removed. You can install it again with the Install button.</p>' +
|
||||
removedInfo + preservedInfo +
|
||||
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Close</button></div>';
|
||||
} else {
|
||||
showAlert('Error: ' + (data.error || 'Unknown error'));
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Remove';
|
||||
}
|
||||
} catch (err) {
|
||||
showAlert('Network error: ' + err.message);
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Remove';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,662 @@
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="hu" class="no-js">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AdventureLog — Felhom.eu</title>
|
||||
|
||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg?v=0.261.0">
|
||||
<link rel="stylesheet" href="/static/style.css?v=0.261.0">
|
||||
<meta name="csrf-token" content="f1b38af3981c5aa0730617a1677adcfa060cef61805c532aa5f440be5feef25a">
|
||||
<script>
|
||||
|
||||
|
||||
document.documentElement.className=document.documentElement.className.replace('no-js','js');
|
||||
function csrfHeaders(){var el=document.querySelector('meta[name="csrf-token"]');return el?{'X-CSRF-Token':el.content}:{};}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function felhomConfirm(el,question,onYes){
|
||||
if(!el||el.dataset.fcOpen)return;
|
||||
el.dataset.fcOpen='1';
|
||||
var wrap=document.createElement('span');wrap.className='inline-confirm';
|
||||
var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question;
|
||||
var yes=document.createElement('button');yes.type='button';yes.className='btn btn-xs btn-danger';yes.textContent='Igen';
|
||||
var no=document.createElement('button');no.type='button';no.className='btn btn-xs btn-outline';no.textContent='Mégse';
|
||||
wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no);
|
||||
el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling);
|
||||
function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;}
|
||||
no.addEventListener('click',close);
|
||||
yes.addEventListener('click',function(){close();onYes();});
|
||||
}
|
||||
document.addEventListener('click',function(e){
|
||||
var btn=e.target.closest?e.target.closest('[data-confirm]'):null;
|
||||
if(!btn)return;
|
||||
e.preventDefault();
|
||||
felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){
|
||||
var form=btn.closest('form');
|
||||
if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
document.addEventListener('click',function(e){
|
||||
var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null;
|
||||
if(!btn)return;
|
||||
var group=btn.closest('.nav-group');
|
||||
if(!group)return;
|
||||
var willOpen=!group.classList.contains('is-open');
|
||||
document.querySelectorAll('.nav-group.is-open').forEach(function(g){
|
||||
g.classList.remove('is-open');
|
||||
var t=g.querySelector('.nav-group-toggle');
|
||||
if(t)t.setAttribute('aria-expanded','false');
|
||||
});
|
||||
if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');}
|
||||
});
|
||||
function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML='<h3>Üzenet</h3><pre style="white-space:pre-wrap;word-break:break-word;background:var(--bg-2);padding:.75rem;border-radius:.375rem;font-size:.85rem;max-height:60vh;overflow-y:auto;user-select:text;cursor:text">'+msg.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')+'</pre><div class="modal-actions"><button class="btn btn-primary" onclick="document.getElementById(\'alert-modal\').remove()">OK</button></div>';o.appendChild(c);document.body.appendChild(o);}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display:none" aria-hidden="true">
|
||||
<symbol id="i-hard-drive" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 16h.01" /> <path d="M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" /> <path d="M21.946 12.013H2.054" /> <path d="M6 16h.01" /></symbol>
|
||||
<symbol id="i-cpu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20v2" /> <path d="M12 2v2" /> <path d="M17 20v2" /> <path d="M17 2v2" /> <path d="M2 12h2" /> <path d="M2 17h2" /> <path d="M2 7h2" /> <path d="M20 12h2" /> <path d="M20 17h2" /> <path d="M20 7h2" /> <path d="M7 20v2" /> <path d="M7 2v2" /> <rect x="4" y="4" width="16" height="16" rx="2" /> <rect x="8" y="8" width="8" height="8" rx="1" /></symbol>
|
||||
<symbol id="i-memory-stick" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 12v-2" /> <path d="M12 18v-2" /> <path d="M16 12v-2" /> <path d="M16 18v-2" /> <path d="M2 11h1.5" /> <path d="M20 18v-2" /> <path d="M20.5 11H22" /> <path d="M4 18v-2" /> <path d="M8 12v-2" /> <path d="M8 18v-2" /> <rect x="2" y="6" width="20" height="10" rx="2" /></symbol>
|
||||
<symbol id="i-thermometer" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 4v10.54a4 4 0 1 1-4 0V4a2 2 0 0 1 4 0Z" /></symbol>
|
||||
<symbol id="i-triangle-alert" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" /> <path d="M12 9v4" /> <path d="M12 17h.01" /></symbol>
|
||||
<symbol id="i-lock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="11" x="3" y="11" rx="2" ry="2" /> <path d="M7 11V7a5 5 0 0 1 10 0v4" /></symbol>
|
||||
<symbol id="i-cloud" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z" /></symbol>
|
||||
<symbol id="i-square" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" /></symbol>
|
||||
<symbol id="i-rotate-cw" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" /> <path d="M21 3v5h-5" /></symbol>
|
||||
<symbol id="i-file-text" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /> <path d="M14 2v5a1 1 0 0 0 1 1h5" /> <path d="M10 9H8" /> <path d="M16 13H8" /> <path d="M16 17H8" /></symbol>
|
||||
<symbol id="i-external-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6" /> <path d="M10 14 21 3" /> <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></symbol>
|
||||
<symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /></symbol>
|
||||
<symbol id="i-server" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="8" x="2" y="2" rx="2" ry="2" /> <rect width="20" height="8" x="2" y="14" rx="2" ry="2" /> <line x1="6" x2="6.01" y1="6" y2="6" /> <line x1="6" x2="6.01" y1="18" y2="18" /></symbol>
|
||||
<symbol id="i-share" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3" /> <circle cx="6" cy="12" r="3" /> <circle cx="18" cy="19" r="3" /> <line x1="8.59" x2="15.42" y1="13.51" y2="17.49" /> <line x1="15.41" x2="8.59" y1="6.51" y2="10.49" /></symbol>
|
||||
<symbol id="i-layout-grid" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="7" x="3" y="3" rx="1" /> <rect width="7" height="7" x="14" y="3" rx="1" /> <rect width="7" height="7" x="14" y="14" rx="1" /> <rect width="7" height="7" x="3" y="14" rx="1" /></symbol>
|
||||
<symbol id="i-rocket" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91 0z" /> <path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z" /> <path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0" /> <path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5" /></symbol>
|
||||
<symbol id="i-settings" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /> <circle cx="12" cy="12" r="3" /></symbol>
|
||||
<symbol id="i-bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.268 21a2 2 0 0 0 3.464 0" /> <path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" /></symbol>
|
||||
<symbol id="i-x" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18" /> <path d="m6 6 12 12" /></symbol>
|
||||
<symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5" /></symbol>
|
||||
<symbol id="i-pencil" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" /> <path d="m15 5 4 4" /></symbol>
|
||||
<symbol id="i-info" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /> <path d="M12 16v-4" /> <path d="M12 8h.01" /></symbol>
|
||||
<symbol id="i-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 15V3" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /> <path d="m7 10 5 5 5-5" /></symbol>
|
||||
<symbol id="i-upload" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v12" /> <path d="m17 8-5-5-5 5" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /></symbol>
|
||||
<symbol id="i-trash-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 11v6" /> <path d="M14 11v6" /> <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /> <path d="M3 6h18" /> <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></symbol>
|
||||
<symbol id="i-wrench" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z" /></symbol>
|
||||
<symbol id="i-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" /> <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" /></symbol>
|
||||
<symbol id="i-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34" /> <circle cx="11" cy="11" r="8" /></symbol>
|
||||
<symbol id="i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14" /> <path d="M12 5v14" /></symbol>
|
||||
<symbol id="i-chevron-down" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6" /></symbol>
|
||||
<symbol id="i-play" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z" /></symbol>
|
||||
<symbol id="i-pause" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="14" y="3" width="5" height="18" rx="1" /> <rect x="5" y="3" width="5" height="18" rx="1" /></symbol>
|
||||
<symbol id="i-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h16" /> <path d="M4 6h16" /> <path d="M4 18h16" /></symbol>
|
||||
</svg>
|
||||
<header class="mobile-topbar">
|
||||
<a href="/" class="mobile-topbar-logo"><img src="/static/felhom-logo.svg?v=0.261.0" alt="Felhom.eu"></a>
|
||||
<button type="button" class="nav-burger" aria-expanded="false" aria-controls="sidebar" aria-label="Menü">
|
||||
<svg class="ico"><use href="#i-menu"/></svg>
|
||||
</button>
|
||||
</header>
|
||||
<div class="nav-backdrop" hidden></div>
|
||||
<nav class="sidebar" id="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<img src="/static/felhom-logo.svg?v=0.261.0" alt="Felhom.eu" class="sidebar-logo">
|
||||
</div>
|
||||
<ul class="nav-links">
|
||||
<li><a href="/launcher" class=""><svg class="ico"><use href="#i-rocket"/></svg>Indítópult</a></li>
|
||||
<li><a href="/dashboard" class=""><svg class="ico"><use href="#i-layout-grid"/></svg>Vezérlőpult</a></li>
|
||||
<li><a href="/stacks" class="active"><svg class="ico"><use href="#i-cloud"/></svg>Alkalmazások</a></li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-storage"><svg class="ico"><use href="#i-hard-drive"/></svg>Tárhely<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-storage" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/storage" class="">Meghajtók</a></li>
|
||||
<li><a href="/storage/network" class="">Hálózati tárhely</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-backups"><svg class="ico"><use href="#i-shield"/></svg>Biztonsági mentés<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-backups" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/backups" class="">Áttekintés</a></li>
|
||||
<li><a href="/backups/remote" class="">Távoli mentés</a></li>
|
||||
<li><a href="/backups/apps" class="">Alkalmazások</a></li>
|
||||
<li><a href="/backups/restore" class="">Visszaállítás</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-sharing"><svg class="ico"><use href="#i-share"/></svg>Megosztás<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-sharing" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/sharing" class="">Hálózati megosztás</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/monitoring" class=""><svg class="ico"><use href="#i-cpu"/></svg>Rendszermonitor</a></li>
|
||||
<li><a href="/debug" class=""><svg class="ico"><use href="#i-wrench"/></svg>Debug</a></li>
|
||||
</ul>
|
||||
<div class="sidebar-bottom">
|
||||
<div class="nav-group-label">Beállítások</div>
|
||||
<ul class="nav-links nav-links-sub">
|
||||
<li><a href="/settings" class=""><svg class="ico"><use href="#i-settings"/></svg>Rendszer</a></li>
|
||||
<li><a href="/settings/notifications" class=""><svg class="ico"><use href="#i-bell"/></svg>Értesítések</a></li>
|
||||
<li><a href="/settings/security" class=""><svg class="ico"><use href="#i-lock"/></svg>Biztonság és hozzáférés</a></li>
|
||||
</ul>
|
||||
<div class="sidebar-footer">
|
||||
<span class="version">0.261.0</span><details class="lang-globe">
|
||||
<summary class="lang-globe-btn" aria-label="Nyelv" title="Nyelv"><svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10" /><path d="M2 12h20" /><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" /></svg></summary>
|
||||
<ul class="lang-globe-menu">
|
||||
<li><form method="POST" action="/settings/language"><input type="hidden" name="_csrf" value="f1b38af3981c5aa0730617a1677adcfa060cef61805c532aa5f440be5feef25a"><input type="hidden" name="back" value="/apps/adventurelog"><button type="submit" name="lang" value="hu" lang="hu" class="lang-globe-item current" aria-current="true">Magyar</button></form></li>
|
||||
<li><form method="POST" action="/settings/language"><input type="hidden" name="_csrf" value="f1b38af3981c5aa0730617a1677adcfa060cef61805c532aa5f440be5feef25a"><input type="hidden" name="back" value="/apps/adventurelog"><button type="submit" name="lang" value="en" lang="en" class="lang-globe-item">English</button></form></li>
|
||||
</ul>
|
||||
</details>
|
||||
<a href="/logout" class="logout-link">Kijelentkezés ↗</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<main class="content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="alerts-container">
|
||||
|
||||
|
||||
<div class="alert-banner alert-banner-warning">
|
||||
<span class="alert-icon"><svg class="ico"><use href="#i-triangle-alert"/></svg></span>
|
||||
<span class="alert-message">Telepített alkalmazás nem fut: Tandoor Recipes (stopped)</span>
|
||||
<a href="/monitoring" class="alert-link">Rendszermonitor →</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="alert-banner alert-banner-warning">
|
||||
<span class="alert-icon"><svg class="ico"><use href="#i-triangle-alert"/></svg></span>
|
||||
<span class="alert-message">Hub kapcsolat kikapcsolva — a központi monitoring nem aktív</span>
|
||||
<a href="/monitoring" class="alert-link">Rendszermonitor →</a>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="page-header">
|
||||
<div style="display:flex;align-items:center;gap:1rem">
|
||||
<a href="/stacks" class="btn btn-sm btn-outline">← Alkalmazások</a>
|
||||
<h2>AdventureLog</h2>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:.5rem">
|
||||
|
||||
<span class="stack-state-badge state-run">Fut</span>
|
||||
|
||||
|
||||
<span class="tag tag-warn" title="Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.">Frissítés elérhető — 65 napja</span>
|
||||
<a href="https://travel.enkisfelhom.hu" target="_blank" class="btn btn-sm btn-outline">Megnyitás ↗</a>
|
||||
<a href="/stacks/adventurelog/logs" class="btn btn-sm btn-outline">Napló</a>
|
||||
|
||||
<a href="/stacks/adventurelog/export" class="btn btn-sm btn-outline">Exportálás</a>
|
||||
<a href="/stacks/adventurelog/deploy" class="btn btn-sm btn-outline">Beállítások</a>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-hero">
|
||||
<img class="app-info-logo" src="/static/assets/adventurelog-logo.svg"
|
||||
alt="AdventureLog" data-fallback="/static/app-placeholder.svg"
|
||||
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/adventurelog-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
||||
<div class="app-info-hero-text">
|
||||
|
||||
<p class="app-info-tagline">Utazási napló - utazások dokumentálása térképpel és fotókkal</p>
|
||||
|
||||
<div class="stack-meta-badges">
|
||||
<span class="meta-badge">~100M RAM</span>
|
||||
<span class="meta-badge">travel</span>
|
||||
|
||||
<span class="meta-badge meta-badge-ok">Pi kompatibilis</span>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="app-screenshots" id="screenshots">
|
||||
<img src="/static/assets/adventurelog-screenshot-1.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
<img src="/static/assets/adventurelog-screenshot-2.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
<img src="/static/assets/adventurelog-screenshot-3.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card" style="margin-top:1rem">
|
||||
<h3>Áthelyezés másik tárhelyre</h3>
|
||||
<p class="form-hint">Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át. Az alkalmazás az áthelyezés alatt rövid időre leáll, az adatok pedig csak az ellenőrzés és a sikeres újraindítás után törlődnek a régi helyről.</p>
|
||||
<div style="display:flex;gap:.5rem;align-items:center;flex-wrap:wrap;margin-top:.5rem">
|
||||
<select id="app-migrate-target" class="btn btn-sm btn-outline">
|
||||
<option value="">Válassz céltárhelyet…</option>
|
||||
<option value="/mnt/felhom-drives/scratch_hdd">SCRATCH HDD (/mnt/felhom-drives/scratch_hdd)</option>
|
||||
</select>
|
||||
<button class="btn btn-sm btn-outline" onclick="appMigrate(this,'adventurelog','AdventureLog')">Áthelyezés</button>
|
||||
</div>
|
||||
<div id="app-migrate-progress" style="display:none;margin-top:.75rem;padding:.75rem;border:1px solid var(--blue);border-radius:6px;background:rgba(0,136,204,0.06)"></div>
|
||||
</div>
|
||||
<script>
|
||||
function appMigFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; }
|
||||
function appMigRender(job){
|
||||
var names={stop:'Leállítás',copy:'Adatok másolása',verify:'Ellenőrzés',flip:'Újratelepítés',redeploy:'Újratelepítés',cleanup:'Régi adatok törlése'};
|
||||
var s=names[job.phase]||job.phase;
|
||||
if(job.phase==='copy'&&job.bytes_total>0){ s+=' ('+Math.floor(100*job.bytes_done/job.bytes_total)+'% — '+appMigFmtGB(job.bytes_done)+'/'+appMigFmtGB(job.bytes_total)+')'; }
|
||||
return s+'…';
|
||||
}
|
||||
function appMigWatch(){
|
||||
var panel=document.getElementById('app-migrate-progress');
|
||||
if(panel) panel.style.display='block';
|
||||
function tick(){
|
||||
fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){
|
||||
var job=d.data&&d.data.job;
|
||||
if(!job){ if(panel) panel.textContent='Nincs folyamatban áthelyezés.'; return; }
|
||||
if(panel) panel.innerHTML=appMigRender(job);
|
||||
if(job.phase==='done'){ if(panel) panel.innerHTML+='<br><strong>Kész</strong>'; setTimeout(function(){location.reload();},1500); return; }
|
||||
if(job.phase==='aborted'){ if(panel) panel.innerHTML+='<br><strong style="color:var(--danger,#c0392b)">Megszakadt: '+(job.error||'')+'</strong><br>A régi adatok érintetlenek.'; return; }
|
||||
setTimeout(tick,1500);
|
||||
}).catch(function(){ setTimeout(tick,2000); });
|
||||
}
|
||||
tick();
|
||||
}
|
||||
function appMigrate(btn,app,label){
|
||||
var sel=document.getElementById('app-migrate-target');
|
||||
var target=sel?sel.value:'';
|
||||
if(!target){ showAlert('Válassz céltárhelyet.'); return; }
|
||||
felhomConfirm(btn,'Áthelyezed a(z) '+label+' adatait ide: '+target+'? Az alkalmazás rövid időre leáll. A régi adatok csak sikeres áthelyezés után törlődnek.',function(){
|
||||
fetch('/api/storage/migrate-app',{method:'POST',headers:Object.assign({'Content-Type':'application/json'},csrfHeaders()),body:JSON.stringify({app:app,target:target})})
|
||||
.then(function(r){return r.json();}).then(function(d){ if(d.ok){ appMigWatch(); } else { showAlert('Hiba: '+(d.error||'ismeretlen')); } })
|
||||
.catch(function(e){ showAlert('Hiba: '+e); });
|
||||
});
|
||||
}
|
||||
(function(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ if(d.data&&d.data.job){ appMigWatch(); } }).catch(function(){}); })();
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<div class="app-info-grid">
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>Mire használható?</h3>
|
||||
<ul class="app-info-list">
|
||||
<li>Utazások dokumentálása képekkel és jegyzetekkel</li><li>Térképes megjelenítés a meglátogatott helyekről</li><li>Úti tervek készítése és szervezése</li><li>Statisztikák - meglátogatott országok, városok</li><li>Kalandok megosztása családdal és barátokkal</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>Első lépések</h3>
|
||||
<ol class="app-info-list">
|
||||
<li>Nyisd meg a travel.DOMAIN címet a böngészőben</li><li>Hozd létre a fiókodat</li><li>Add hozzá az első utazásodat</li><li>Jelöld meg a meglátogatott helyeket a térképen</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>Dokumentáció</h3>
|
||||
<p><a href="https://adventurelog.app/docs/" target="_blank" class="app-info-link">Hivatalos dokumentáció ↗</a></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
(function(){
|
||||
var burger=document.querySelector('.nav-burger');
|
||||
var sidebar=document.getElementById('sidebar');
|
||||
var backdrop=document.querySelector('.nav-backdrop');
|
||||
if(!burger||!sidebar||!backdrop)return;
|
||||
function setOpen(open){
|
||||
sidebar.classList.toggle('is-open',open);
|
||||
document.body.classList.toggle('nav-open',open);
|
||||
backdrop.hidden=!open;
|
||||
burger.setAttribute('aria-expanded',open?'true':'false');
|
||||
}
|
||||
burger.addEventListener('click',function(){setOpen(!sidebar.classList.contains('is-open'));});
|
||||
backdrop.addEventListener('click',function(){setOpen(false);});
|
||||
document.addEventListener('keydown',function(e){
|
||||
if(e.key==='Escape'&&sidebar.classList.contains('is-open'))setOpen(false);
|
||||
});
|
||||
})();
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.closest('a, button, .btn, input, select, textarea, .app-row-actions, .stack-detail-actions')) return;
|
||||
var card = e.target.closest('[data-href]');
|
||||
if (card) window.location.href = card.dataset.href;
|
||||
});
|
||||
async function checkBeforeDeploy(e, name) {
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name);
|
||||
var data = await resp.json();
|
||||
if (data.ok && data.data && data.data.deployed) {
|
||||
e.preventDefault();
|
||||
showAlert('Ez az alkalmazás már telepítve van.');
|
||||
window.location.reload();
|
||||
return false;
|
||||
}
|
||||
} catch(err) {}
|
||||
return true;
|
||||
}
|
||||
async function syncTemplates() {
|
||||
const btn = document.getElementById('sync-btn');
|
||||
const toast = document.getElementById('sync-toast');
|
||||
if (!btn) return;
|
||||
const origText = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '↻ Frissítés...';
|
||||
btn.classList.add('loading');
|
||||
try {
|
||||
const resp = await fetch('/api/sync', {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders())
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (toast) {
|
||||
toast.textContent = data.ok ? (data.message || 'Sablonok frissítve') : ('Hiba: ' + (data.error || 'Ismeretlen hiba'));
|
||||
toast.className = 'sync-toast ' + (data.ok ? 'sync-toast-ok' : 'sync-toast-err');
|
||||
toast.style.display = 'block';
|
||||
setTimeout(function() { toast.style.display = 'none'; }, 5000);
|
||||
}
|
||||
if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) {
|
||||
setTimeout(function() { window.location.reload(); }, 1500);
|
||||
}
|
||||
} catch (err) {
|
||||
if (toast) {
|
||||
toast.textContent = 'Hálózati hiba: ' + err.message;
|
||||
toast.className = 'sync-toast sync-toast-err';
|
||||
toast.style.display = 'block';
|
||||
setTimeout(function() { toast.style.display = 'none'; }, 5000);
|
||||
}
|
||||
}
|
||||
btn.innerHTML = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
}
|
||||
async function stackAction(event, name, action) {
|
||||
const btn = event.currentTarget;
|
||||
const origText = btn.textContent;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Folyamatban...';
|
||||
btn.classList.add('loading');
|
||||
try {
|
||||
const resp = await fetch('/api/stacks/' + name + '/' + action, {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders())
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (!data.ok) {
|
||||
showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba'));
|
||||
btn.textContent = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (action === 'update') {
|
||||
followUpdate(name, btn);
|
||||
return;
|
||||
}
|
||||
window.location.reload();
|
||||
} catch (err) {
|
||||
showAlert('Hálózati hiba: ' + err.message);
|
||||
btn.textContent = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
}
|
||||
}
|
||||
function followUpdate(name, btn) {
|
||||
var started = Date.now();
|
||||
var timer = setInterval(async function() {
|
||||
if (Date.now() - started > 30 * 60 * 1000) { clearInterval(timer); window.location.reload(); return; }
|
||||
try {
|
||||
var r = await fetch('/api/stacks/' + name);
|
||||
var d = await r.json();
|
||||
if (!d.ok || !d.data) return;
|
||||
if (d.data.update_phase_label) btn.textContent = d.data.update_phase_label;
|
||||
if (!d.data.updating) { clearInterval(timer); window.location.reload(); }
|
||||
} catch (e) {}
|
||||
}, 3000);
|
||||
}
|
||||
async function deleteOrphanStack(name) {
|
||||
var modal = document.createElement('div');
|
||||
modal.className = 'modal-overlay';
|
||||
modal.id = 'delete-modal';
|
||||
modal.innerHTML = '<div class="modal-card"><h3>Betöltés...</h3></div>';
|
||||
modal.addEventListener('click', function(e) { if (e.target === modal) closeDeleteModal(); });
|
||||
document.body.appendChild(modal);
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name + '/hdd-data');
|
||||
var data = await resp.json();
|
||||
var hddInfo = '';
|
||||
var checkboxHTML = '';
|
||||
if (data.ok && data.data && data.data.has_hdd_data) {
|
||||
hddInfo = '<div class="modal-hdd-info"><strong>Felhasználói adatok a merevlemezen:</strong>';
|
||||
data.data.hdd_paths.forEach(function(p) {
|
||||
hddInfo += '<div class="modal-hdd-path">' + p.path + ' (' + (p.exists ? p.size_human : 'nem létezik') + ')</div>';
|
||||
});
|
||||
hddInfo += '</div>';
|
||||
checkboxHTML = '<label class="modal-checkbox"><input type="checkbox" id="delete-hdd-check"> Felhasználói adatok törlése a merevlemezről</label>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Alkalmazás törlése: ' + name + '</h3>' +
|
||||
'<p style="color:var(--text-2);font-size:.9rem;margin-bottom:.75rem">Ez a művelet eltávolítja a konténereket, a köteteket és a konfigurációs fájlokat.</p>' +
|
||||
'<div class="alert alert-warning" style="margin-bottom:.75rem">Ez a művelet nem visszavonható!</div>' +
|
||||
hddInfo + checkboxHTML +
|
||||
'<div class="modal-actions">' +
|
||||
'<button class="btn btn-outline" onclick="closeDeleteModal()">Mégsem</button>' +
|
||||
'<button class="btn btn-danger" id="confirm-delete-btn" onclick="confirmDelete(\'' + name + '\')">Törlés</button>' +
|
||||
'</div>';
|
||||
} catch (err) {
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Hiba</h3><p style="color:var(--text-2)">Nem sikerült lekérni az adatokat: ' + err.message + '</p>' +
|
||||
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeDeleteModal()">Bezárás</button></div>';
|
||||
}
|
||||
}
|
||||
function closeDeleteModal() {
|
||||
var modal = document.getElementById('delete-modal');
|
||||
if (modal) modal.remove();
|
||||
}
|
||||
async function confirmDelete(name) {
|
||||
var btn = document.getElementById('confirm-delete-btn');
|
||||
var checkbox = document.getElementById('delete-hdd-check');
|
||||
var removeHDD = checkbox ? checkbox.checked : false;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Törlés folyamatban...';
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name, {
|
||||
method: 'DELETE',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({remove_hdd_data: removeHDD})
|
||||
});
|
||||
var data = await resp.json();
|
||||
if (data.ok) {
|
||||
var modal = document.getElementById('delete-modal');
|
||||
var removedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) {
|
||||
removedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Törölt adatok: ' + data.data.hdd_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
var preservedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) {
|
||||
preservedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Megőrzött adatok: ' + data.data.hdd_paths_preserved.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.hdd_note) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">' + data.data.hdd_note + '</p>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Sikeresen törölve!</h3>' +
|
||||
'<p style="color:var(--text-2)">Az alkalmazás (' + name + ') törölve lett.</p>' +
|
||||
removedInfo + preservedInfo +
|
||||
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Bezárás</button></div>';
|
||||
} else {
|
||||
showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba'));
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Törlés';
|
||||
}
|
||||
} catch (err) {
|
||||
showAlert('Hálózati hiba: ' + err.message);
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Törlés';
|
||||
}
|
||||
}
|
||||
async function removeStack(name) {
|
||||
var modal = document.createElement('div');
|
||||
modal.className = 'modal-overlay';
|
||||
modal.id = 'remove-modal';
|
||||
modal.innerHTML = '<div class="modal-card"><h3>Betöltés...</h3></div>';
|
||||
modal.addEventListener('click', function(e) { if (e.target === modal) closeRemoveModal(); });
|
||||
document.body.appendChild(modal);
|
||||
try {
|
||||
var [hddResp, backupResp] = await Promise.all([
|
||||
fetch('/api/stacks/' + name + '/hdd-data').then(function(r) { return r.json(); }),
|
||||
fetch('/api/stacks/' + name + '/backup-data').then(function(r) { return r.json(); })
|
||||
]);
|
||||
var sections = '';
|
||||
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Mindig törlődik:</strong>' +
|
||||
'<ul style="margin:.25rem 0;padding-left:1.2rem;color:var(--text-2);font-size:.85rem">' +
|
||||
'<li>Docker kötetek (adatbázis, alkalmazás konfiguráció)</li>' +
|
||||
'<li>Telepítési konfiguráció (app.yaml)</li>' +
|
||||
'<li>Másodlagos mentés ütemezése</li>' +
|
||||
'</ul></div>';
|
||||
|
||||
var hddCheckbox = '';
|
||||
if (hddResp.ok && hddResp.data && hddResp.data.has_hdd_data) {
|
||||
var hddPaths = '';
|
||||
hddResp.data.hdd_paths.forEach(function(p) {
|
||||
hddPaths += '<div class="modal-hdd-path">' + p.path + ' (' + (p.exists ? p.size_human : 'nem létezik') + ')</div>';
|
||||
});
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Felhasználói adatok a merevlemezen:</strong>' + hddPaths +
|
||||
'<label class="modal-checkbox"><input type="checkbox" id="remove-hdd-check"> Felhasználói adatok törlése</label>' +
|
||||
'<div class="alert alert-info" style="margin-top:.5rem;font-size:.8rem" id="remove-hdd-keep-warning">' +
|
||||
'Ha újratelepíti az alkalmazást, az adatokat újra importálnia kell, mivel az adatbázis törlődik. A megtartott adatok a továbbiakban NEM lesznek automatikusan mentve.' +
|
||||
'</div></div>';
|
||||
hddCheckbox = '\x3Cscript>document.getElementById("remove-hdd-check").addEventListener("change",function(){document.getElementById("remove-hdd-keep-warning").style.display=this.checked?"none":"";});<\/script>';
|
||||
}
|
||||
|
||||
var backupCheckbox = '';
|
||||
if (backupResp.ok && backupResp.data && backupResp.data.has_backups) {
|
||||
var bkPaths = '';
|
||||
backupResp.data.backup_paths.forEach(function(p) {
|
||||
if (p.exists) bkPaths += '<div class="modal-hdd-path">' + p.path + ' (' + p.size_human + ')</div>';
|
||||
});
|
||||
if (bkPaths) {
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Mentési adatok:</strong>' + bkPaths +
|
||||
'<label class="modal-checkbox"><input type="checkbox" id="remove-backup-check"> Mentési adatok törlése</label>' +
|
||||
'<div class="alert alert-info" style="margin-top:.5rem;font-size:.8rem">' +
|
||||
'Az éjszakai restic pillanatképek nem törölhetők egyenként — a megőrzési szabályok szerint automatikusan elavulnak.' +
|
||||
'</div></div>';
|
||||
}
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Alkalmazás eltávolítása: ' + name + '</h3>' +
|
||||
'<p style="color:var(--text-2);font-size:.9rem;margin-bottom:.75rem">Az alkalmazás visszaáll "Nincs telepítve" állapotba. A sablon megmarad, újratelepíthető.</p>' +
|
||||
'<div class="alert alert-warning" style="margin-bottom:.75rem">Ez a művelet nem visszavonható!</div>' +
|
||||
sections +
|
||||
'<div class="modal-actions">' +
|
||||
'<button class="btn btn-outline" onclick="closeRemoveModal()">Mégsem</button>' +
|
||||
'<button class="btn btn-danger" id="confirm-remove-btn" onclick="confirmRemoveStack(\'' + name + '\')">Eltávolítás</button>' +
|
||||
'</div>' + hddCheckbox;
|
||||
} catch (err) {
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Hiba</h3><p style="color:var(--text-2)">Nem sikerült lekérni az adatokat: ' + err.message + '</p>' +
|
||||
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeRemoveModal()">Bezárás</button></div>';
|
||||
}
|
||||
}
|
||||
function closeRemoveModal() {
|
||||
var modal = document.getElementById('remove-modal');
|
||||
if (modal) modal.remove();
|
||||
}
|
||||
async function confirmRemoveStack(name) {
|
||||
var btn = document.getElementById('confirm-remove-btn');
|
||||
var hddCheck = document.getElementById('remove-hdd-check');
|
||||
var backupCheck = document.getElementById('remove-backup-check');
|
||||
var removeHDD = hddCheck ? hddCheck.checked : false;
|
||||
var removeBackups = backupCheck ? backupCheck.checked : false;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Eltávolítás folyamatban...';
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name + '/remove', {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({remove_hdd_data: removeHDD, remove_backups: removeBackups})
|
||||
});
|
||||
var data = await resp.json();
|
||||
if (data.ok) {
|
||||
var modal = document.getElementById('remove-modal');
|
||||
var removedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) {
|
||||
removedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Törölt adatok: ' + data.data.hdd_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.backup_paths_removed && data.data.backup_paths_removed.length > 0) {
|
||||
removedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Törölt mentések: ' + data.data.backup_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
var preservedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) {
|
||||
preservedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Megőrzött adatok: ' + data.data.hdd_paths_preserved.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.hdd_note) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">' + data.data.hdd_note + '</p>';
|
||||
}
|
||||
if (data.data && data.data.backup_paths_refused && data.data.backup_paths_refused.length > 0) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Nem törölt mentések: ' + data.data.backup_paths_refused.join('; ') + '</p>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Sikeresen eltávolítva!</h3>' +
|
||||
'<p style="color:var(--text-2)">Az alkalmazás (' + name + ') eltávolítva. Újratelepíthető a Telepítés gombbal.</p>' +
|
||||
removedInfo + preservedInfo +
|
||||
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Bezárás</button></div>';
|
||||
} else {
|
||||
showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba'));
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Eltávolítás';
|
||||
}
|
||||
} catch (err) {
|
||||
showAlert('Hálózati hiba: ' + err.message);
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Eltávolítás';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"hu": [
|
||||
{
|
||||
"title": "Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.",
|
||||
"text": "Frissítés elérhető — ma"
|
||||
}
|
||||
],
|
||||
"en": [
|
||||
{
|
||||
"title": "A newer version of this app is available. Select the Update button to start it.",
|
||||
"text": "Update available — today"
|
||||
}
|
||||
]
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
=== [1] PG_VERSION — the datadir's own record of its major
|
||||
16
|
||||
16
|
||||
=== [2] the engine's REFUSAL, verbatim (the whole point of this leg)
|
||||
|
||||
PostgreSQL Database directory appears to contain a database; Skipping initialization
|
||||
|
||||
2026-09-21 21:44:28.155 CEST [1] LOG: starting PostgreSQL 16.15 on x86_64-pc-linux-musl, compiled by gcc (Alpine 15.2.0) 15.2.0, 64-bit
|
||||
2026-09-21 21:44:28.155 CEST [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
|
||||
2026-09-21 21:44:28.155 CEST [1] LOG: listening on IPv6 address "::", port 5432
|
||||
2026-09-21 21:44:28.161 CEST [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
|
||||
2026-09-21 21:44:28.169 CEST [29] LOG: database system was shut down at 2026-09-21 21:42:44 CEST
|
||||
2026-09-21 21:44:28.183 CEST [1] LOG: database system is ready to accept connections
|
||||
=== [3] container state
|
||||
image=postgres:16-alpine running=true restarts=0 exit=0
|
||||
=== [4] the version binary in the image
|
||||
postgres (PostgreSQL) 16.15
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
=== [1] PG_VERSION — the datadir's own record of its major
|
||||
Error response from daemon: No such container: docmost-postgres
|
||||
docker: invalid spec: :/d: empty section between colons
|
||||
|
||||
Run 'docker run --help' for more information
|
||||
=== [2] the engine's REFUSAL, verbatim (the whole point of this leg)
|
||||
Error response from daemon: No such container: docmost-postgres
|
||||
=== [3] container state
|
||||
|
||||
error: no such object: docmost-postgres
|
||||
=== [4] the version binary in the image
|
||||
Error response from daemon: No such container: docmost-postgres
|
||||
(cannot exec — not running)
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
=== [1] PG_VERSION — the datadir's own record of its major
|
||||
16
|
||||
Unable to find image 'alpine:3.20' locally
|
||||
3.20: Pulling from library/alpine
|
||||
25f1d6b1951a: Already exists
|
||||
Digest: sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc
|
||||
Status: Downloaded newer image for alpine:3.20
|
||||
16
|
||||
=== [2] the engine's REFUSAL, verbatim (the whole point of this leg)
|
||||
done
|
||||
server started
|
||||
CREATE DATABASE
|
||||
|
||||
|
||||
/usr/local/bin/docker-entrypoint.sh: ignoring /docker-entrypoint-initdb.d/*
|
||||
|
||||
waiting for server to shut down....2026-09-21 21:42:03.450 CEST [41] LOG: received fast shutdown request
|
||||
2026-09-21 21:42:03.453 CEST [41] LOG: aborting any active transactions
|
||||
2026-09-21 21:42:03.456 CEST [41] LOG: background worker "logical replication launcher" (PID 47) exited with exit code 1
|
||||
2026-09-21 21:42:03.457 CEST [42] LOG: shutting down
|
||||
2026-09-21 21:42:03.459 CEST [42] LOG: checkpoint starting: shutdown immediate
|
||||
2026-09-21 21:42:03.544 CEST [42] LOG: checkpoint complete: wrote 926 buffers (5.7%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.022 s, sync=0.051 s, total=0.088 s; sync files=301, longest=0.022 s, average=0.001 s; distance=4283 kB, estimate=4283 kB; lsn=0/1925D58, redo lsn=0/1925D58
|
||||
2026-09-21 21:42:03.558 CEST [41] LOG: database system is shut down
|
||||
done
|
||||
server stopped
|
||||
|
||||
PostgreSQL init process complete; ready for start up.
|
||||
|
||||
2026-09-21 21:42:03.687 CEST [1] LOG: starting PostgreSQL 16.15 on x86_64-pc-linux-musl, compiled by gcc (Alpine 15.2.0) 15.2.0, 64-bit
|
||||
2026-09-21 21:42:03.687 CEST [1] LOG: listening on IPv4 address "0.0.0.0", port 5432
|
||||
2026-09-21 21:42:03.687 CEST [1] LOG: listening on IPv6 address "::", port 5432
|
||||
2026-09-21 21:42:03.692 CEST [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
|
||||
2026-09-21 21:42:03.701 CEST [57] LOG: database system was shut down at 2026-09-21 21:42:03 CEST
|
||||
2026-09-21 21:42:03.710 CEST [1] LOG: database system is ready to accept connections
|
||||
=== [3] container state
|
||||
image=postgres:16-alpine running=true restarts=0 exit=0
|
||||
=== [4] the version binary in the image
|
||||
postgres (PostgreSQL) 16.15
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"hu": [
|
||||
"Docmost — Felhom.eu document.documentElement.className=document.documentElement.className.replace('no-js','js'); function csrfHeaders(){var el=document.querySelector('meta[name=\"csrf-token\"]');return el?{'X-CSRF-Token':el.content}:{};} function felhomConfirm(el,question,onYes){ if(!el||el.dataset.fcOpen)return; el.dataset.fcOpen='1'; var wrap=document.createElement('span');wrap.className='inline-confirm'; var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question; var yes=document.createElement('button');yes.type='button';yes.className='btn btn-xs btn-danger';yes.textContent='Igen'; var no=document.createElement('button');no.type='button';no.className='btn btn-xs btn-outline';no.textContent='Mégse'; wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no); el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling); function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;} no.addEventListener('click',close); yes.addEventListener('click',function(){close();onYes();}); } document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('[data-confirm]'):null; if(!btn)return; e.preventDefault(); felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){ var form=btn.closest('form'); if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();} }); }); document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null; if(!btn)return; var group=btn.closest('.nav-group'); if(!group)return; var willOpen=!group.classList.contains('is-open'); document.querySelectorAll('.nav-group.is-open').forEach(function(g){ g.classList.remove('is-open'); var t=g.querySelector('.nav-group-toggle'); if(t)t.setAttribute('aria-expanded','false'); }); if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');} }); function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML=' Üzenet '+msg.replace(/&/g,'&').replace(/ /g,'>')+' OK ';o.appendChild(c);document.body.appendChild(o);} Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások Docmost Leállítva Frissítés elérhető — ma Megnyitás ↗ Napló Exportálás Beállítások A(z) docmost frissítése 2026-09-21 21:44-kor nem sikerült, és az alkalmazás nem indult el az új verzióval.",
|
||||
"Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:44 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"Mentések Modern wiki és dokumentáció platform Notion-szerű szerkesztővel ~200M RAM productivity Csak x86 Áthelyezés másik tárhelyre Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át.",
|
||||
"Válassz céltárhelyet… SCRATCH HDD (/mnt/felhom-drives/scratch_hdd) Áthelyezés function appMigFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; } function appMigRender(job){ var names={stop:'Leállítás',copy:'Adatok másolása',verify:'Ellenőrzés',flip:'Újratelepítés',redeploy:'Újratelepítés',cleanup:'Régi adatok törlése'}; var s=names[job.phase]||job.phase; if(job.phase==='copy'&&job.bytes_total>0){ s+=' ('+Math.floor(100*job.bytes_done/job.bytes_total)+'% — '+appMigFmtGB(job.bytes_done)+'/'+appMigFmtGB(job.bytes_total)+')'; } return s+'…'; } function appMigWatch(){ var panel=document.getElementById('app-migrate-progress'); if(panel) panel.style.display='block'; function tick(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ var job=d.data&&d.data.job; if(!job){ if(panel) panel.textContent='Nincs folyamatban áthelyezés.'; return; } if(panel) panel.innerHTML=appMigRender(job); if(job.phase==='done'){ if(panel) panel.innerHTML+=' Kész '; setTimeout(function(){location.reload();},1500); return; } if(job.phase==='aborted'){ if(panel) panel.innerHTML+=' Megszakadt: '+(job.error||'')+' A régi adatok érintetlenek.'; return; } setTimeout(tick,1500); }).catch(function(){ setTimeout(tick,2000); }); } tick(); } function appMigrate(btn,app,label){ var sel=document.getElementById('app-migrate-target'); var target=sel?sel.value:''; if(!target){ showAlert('Válassz céltárhelyet.'); return; } felhomConfirm(btn,'Áthelyezed a(z) '+label+' adatait ide: '+target+'?",
|
||||
"A régi adatok csak sikeres áthelyezés után törlődnek.',function(){ fetch('/api/storage/migrate-app',{method:'POST',headers:Object.assign({'Content-Type':'application/json'},csrfHeaders()),body:JSON.stringify({app:app,target:target})}) .then(function(r){return r.json();}).then(function(d){ if(d.ok){ appMigWatch(); } else { showAlert('Hiba: '+(d.error||'ismeretlen')); } }) .catch(function(e){ showAlert('Hiba: '+e); }); }); } (function(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ if(d.data&&d.data.job){ appMigWatch(); } }).catch(function(){}); })(); Mire használható?",
|
||||
"Csapat tudásbázis és belső dokumentáció Notion-szerű blokk szerkesztő valós idejű együttműködéssel Terek és oldalak hierarchikus szervezése Képek, táblázatok és kód blokkok beágyazása Keresés a teljes dokumentációban Első lépések Nyisd meg a docs.DOMAIN címet a böngészőben Az első regisztrált felhasználó automatikusan admin lesz Hozd létre az első teret (Space) a dokumentumoknak Kezdj el írni a Notion-szerű szerkesztőben Előfeltételek x86 processzor szükséges Legalább 768 MB szabad RAM (Docmost + PostgreSQL + Redis) Dokumentáció Hivatalos dokumentáció ↗ (function(){ var burger=document.querySelector('.nav-burger'); var sidebar=document.getElementById('sidebar'); var backdrop=document.querySelector('.nav-backdrop'); if(!burger||!sidebar||!backdrop)return; function setOpen(open){ sidebar.classList.toggle('is-open',open); document.body.classList.toggle('nav-open',open); backdrop.hidden=!open; burger.setAttribute('aria-expanded',open?'true':'false'); } burger.addEventListener('click',function(){setOpen(!sidebar.classList.contains('is-open'));}); backdrop.addEventListener('click',function(){setOpen(false);}); document.addEventListener('keydown',function(e){ if(e.key==='Escape'&&sidebar.classList.contains('is-open'))setOpen(false); }); })(); document.addEventListener('click', function(e) { if (e.target.closest('a, button, .btn, input, select, textarea, .app-row-actions, .stack-detail-actions')) return; var card = e.target.closest('[data-href]'); if (card) window.location.href = card.dataset.href; }); async function checkBeforeDeploy(e, name) { try { var resp = await fetch('/api/stacks/' + name); var data = await resp.json(); if (data.ok && data.data && data.data.deployed) { e.preventDefault(); showAlert('Ez az alkalmazás már telepítve van.'); window.location.reload(); return false; } } catch(err) {} return true; } async function syncTemplates() { const btn = document.getElementById('sync-btn'); const toast = document.getElementById('sync-toast'); if (!btn) return; const origText = btn.innerHTML; btn.disabled = true; btn.innerHTML = '↻ Frissítés...'; btn.classList.add('loading'); try { const resp = await fetch('/api/sync', { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (toast) { toast.textContent = data.ok ?",
|
||||
"(data.message || 'Sablonok frissítve') : ('Hiba: ' + (data.error || 'Ismeretlen hiba')); toast.className = 'sync-toast ' + (data.ok ?",
|
||||
"'sync-toast-ok' : 'sync-toast-err'); toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) { setTimeout(function() { window.location.reload(); }, 1500); } } catch (err) { if (toast) { toast.textContent = 'Hálózati hiba: ' + err.message; toast.className = 'sync-toast sync-toast-err'; toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } } btn.innerHTML = origText; btn.disabled = false; btn.classList.remove('loading'); } async function stackAction(event, name, action) { const btn = event.currentTarget; const origText = btn.textContent; btn.disabled = true; btn.textContent = 'Folyamatban...'; btn.classList.add('loading'); try { const resp = await fetch('/api/stacks/' + name + '/' + action, { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (!data.ok) { showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba')); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); return; } if (action === 'update') { followUpdate(name, btn); return; } window.location.reload(); } catch (err) { showAlert('Hálózati hiba: ' + err.message); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); } } function followUpdate(name, btn) { var started = Date.now(); var timer = setInterval(async function() { if (Date.now() - started > 30 * 60 * 1000) { clearInterval(timer); window.location.reload(); return; } try { var r = await fetch('/api/stacks/' + name); var d = await r.json(); if (!d.ok || !d.data) return; if (d.data.update_phase_label) btn.textContent = d.data.update_phase_label; if (!d.data.updating) { clearInterval(timer); window.location.reload(); } } catch (e) {} }, 3000); } async function deleteOrphanStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'delete-modal'; modal.innerHTML = ' Betöltés...",
|
||||
"'; modal.addEventListener('click', function(e) { if (e.target === modal) closeDeleteModal(); }); document.body.appendChild(modal); try { var resp = await fetch('/api/stacks/' + name + '/hdd-data'); var data = await resp.json(); var hddInfo = ''; var checkboxHTML = ''; if (data.ok && data.data && data.data.has_hdd_data) { hddInfo = ' Felhasználói adatok a merevlemezen: '; data.data.hdd_paths.forEach(function(p) { hddInfo += ' ' + p.path + ' (' + (p.exists ?",
|
||||
"' + ' Ez a művelet nem visszavonható!",
|
||||
"' + hddInfo + checkboxHTML + ' ' + ' Mégsem ' + ' Törlés ' + ' '; } catch (err) { modal.querySelector('.modal-card').innerHTML = ' Hiba Nem sikerült lekérni az adatokat: ' + err.message + ' ' + ' Bezárás '; } } function closeDeleteModal() { var modal = document.getElementById('delete-modal'); if (modal) modal.remove(); } async function confirmDelete(name) { var btn = document.getElementById('confirm-delete-btn'); var checkbox = document.getElementById('delete-hdd-check'); var removeHDD = checkbox ?",
|
||||
"checkbox.checked : false; btn.disabled = true; btn.textContent = 'Törlés folyamatban...'; try { var resp = await fetch('/api/stacks/' + name, { method: 'DELETE', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()), body: JSON.stringify({remove_hdd_data: removeHDD}) }); var data = await resp.json(); if (data.ok) { var modal = document.getElementById('delete-modal'); var removedInfo = ''; if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) { removedInfo = ' Törölt adatok: ' + data.data.hdd_paths_removed.join(', ') + ' '; } var preservedInfo = ''; if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) { preservedInfo = ' Megőrzött adatok: ' + data.data.hdd_paths_preserved.join(', ') + ' '; } if (data.data && data.data.hdd_note) { preservedInfo += ' ' + data.data.hdd_note + ' '; } modal.querySelector('.modal-card').innerHTML = ' Sikeresen törölve!",
|
||||
"' + removedInfo + preservedInfo + ' Bezárás '; } else { showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba')); btn.disabled = false; btn.textContent = 'Törlés'; } } catch (err) { showAlert('Hálózati hiba: ' + err.message); btn.disabled = false; btn.textContent = 'Törlés'; } } async function removeStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'remove-modal'; modal.innerHTML = ' Betöltés...",
|
||||
"'; modal.addEventListener('click', function(e) { if (e.target === modal) closeRemoveModal(); }); document.body.appendChild(modal); try { var [hddResp, backupResp] = await Promise.all([ fetch('/api/stacks/' + name + '/hdd-data').then(function(r) { return r.json(); }), fetch('/api/stacks/' + name + '/backup-data').then(function(r) { return r.json(); }) ]); var sections = ''; sections += ' ' + ' Mindig törlődik: ' + ' ' + ' Docker kötetek (adatbázis, alkalmazás konfiguráció) ' + ' Telepítési konfiguráció (app.yaml) ' + ' Másodlagos mentés ütemezése ' + ' '; var hddCheckbox = ''; if (hddResp.ok && hddResp.data && hddResp.data.has_hdd_data) { var hddPaths = ''; hddResp.data.hdd_paths.forEach(function(p) { hddPaths += ' ' + p.path + ' (' + (p.exists ?"
|
||||
],
|
||||
"en": [
|
||||
"Docmost — Felhom.eu document.documentElement.className=document.documentElement.className.replace('no-js','js'); function csrfHeaders(){var el=document.querySelector('meta[name=\"csrf-token\"]');return el?{'X-CSRF-Token':el.content}:{};} function felhomConfirm(el,question,onYes){ if(!el||el.dataset.fcOpen)return; el.dataset.fcOpen='1'; var wrap=document.createElement('span');wrap.className='inline-confirm'; var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question; var yes=document.createElement('button');yes.type='button';yes.className='btn btn-xs btn-danger';yes.textContent='Yes'; var no=document.createElement('button');no.type='button';no.className='btn btn-xs btn-outline';no.textContent='Cancel'; wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no); el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling); function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;} no.addEventListener('click',close); yes.addEventListener('click',function(){close();onYes();}); } document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('[data-confirm]'):null; if(!btn)return; e.preventDefault(); felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){ var form=btn.closest('form'); if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();} }); }); document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null; if(!btn)return; var group=btn.closest('.nav-group'); if(!group)return; var willOpen=!group.classList.contains('is-open'); document.querySelectorAll('.nav-group.is-open').forEach(function(g){ g.classList.remove('is-open'); var t=g.querySelector('.nav-group-toggle'); if(t)t.setAttribute('aria-expanded','false'); }); if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');} }); function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML=' Message '+msg.replace(/&/g,'&').replace(/ /g,'>')+' OK ';o.appendChild(c);document.body.appendChild(o);} Launcher Dashboard Apps Storage Drives Network storage Backup Overview Remote backup Apps Restore Sharing Network sharing System monitor Debug Settings System Notifications Security and access 0.261.0 Magyar English Sign out ↗ The hub connection is off — central monitoring is not running System monitor → ← Apps Docmost Stopped Update available — today Open ↗ Log Export Settings A(z) docmost frissítése 2026-09-21 21:44-kor nem sikerült, és az alkalmazás nem indult el az új verzióval.",
|
||||
"Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:44 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"Backups A modern wiki and documentation platform with a Notion-like editor ~200M RAM productivity x86 only Move to another storage You can move this app’s data to another connected storage.",
|
||||
"Choose a target storage… SCRATCH HDD (/mnt/felhom-drives/scratch_hdd) Move function appMigFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; } function appMigRender(job){ var names={stop:'Stopping',copy:'Copying data',verify:'Checking',flip:'Reinstalling',redeploy:'Reinstalling',cleanup:'Deleting old data'}; var s=names[job.phase]||job.phase; if(job.phase==='copy'&&job.bytes_total>0){ s+=' ('+Math.floor(100*job.bytes_done/job.bytes_total)+'% — '+appMigFmtGB(job.bytes_done)+'/'+appMigFmtGB(job.bytes_total)+')'; } return s+'…'; } function appMigWatch(){ var panel=document.getElementById('app-migrate-progress'); if(panel) panel.style.display='block'; function tick(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ var job=d.data&&d.data.job; if(!job){ if(panel) panel.textContent='No move in progress.'; return; } if(panel) panel.innerHTML=appMigRender(job); if(job.phase==='done'){ if(panel) panel.innerHTML+=' Done '; setTimeout(function(){location.reload();},1500); return; } if(job.phase==='aborted'){ if(panel) panel.innerHTML+=' Stopped: '+(job.error||'')+' The old data is untouched.'; return; } setTimeout(tick,1500); }).catch(function(){ setTimeout(tick,2000); }); } tick(); } function appMigrate(btn,app,label){ var sel=document.getElementById('app-migrate-target'); var target=sel?sel.value:''; if(!target){ showAlert('Choose a target storage.'); return; } felhomConfirm(btn,'Move the '+label+' data to: '+target+'?",
|
||||
"The old data is deleted only after a successful move.',function(){ fetch('/api/storage/migrate-app',{method:'POST',headers:Object.assign({'Content-Type':'application/json'},csrfHeaders()),body:JSON.stringify({app:app,target:target})}) .then(function(r){return r.json();}).then(function(d){ if(d.ok){ appMigWatch(); } else { showAlert('Error: '+(d.error||'unknown')); } }) .catch(function(e){ showAlert('Error: '+e); }); }); } (function(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ if(d.data&&d.data.job){ appMigWatch(); } }).catch(function(){}); })(); What is it for?",
|
||||
"A knowledge base and internal documentation for a team A Notion-like block editor several people can use at once Spaces and pages, arranged in a tree Embed images, tables and code blocks Search across all of it First steps Open docs.DOMAIN in your browser The first person to sign up becomes the admin Create your first Space for the documents Start writing in the Notion-like editor Before you start An x86 processor is needed At least 768 MB of free RAM (Docmost + PostgreSQL + Redis) Documentation Official documentation ↗ (function(){ var burger=document.querySelector('.nav-burger'); var sidebar=document.getElementById('sidebar'); var backdrop=document.querySelector('.nav-backdrop'); if(!burger||!sidebar||!backdrop)return; function setOpen(open){ sidebar.classList.toggle('is-open',open); document.body.classList.toggle('nav-open',open); backdrop.hidden=!open; burger.setAttribute('aria-expanded',open?'true':'false'); } burger.addEventListener('click',function(){setOpen(!sidebar.classList.contains('is-open'));}); backdrop.addEventListener('click',function(){setOpen(false);}); document.addEventListener('keydown',function(e){ if(e.key==='Escape'&&sidebar.classList.contains('is-open'))setOpen(false); }); })(); document.addEventListener('click', function(e) { if (e.target.closest('a, button, .btn, input, select, textarea, .app-row-actions, .stack-detail-actions')) return; var card = e.target.closest('[data-href]'); if (card) window.location.href = card.dataset.href; }); async function checkBeforeDeploy(e, name) { try { var resp = await fetch('/api/stacks/' + name); var data = await resp.json(); if (data.ok && data.data && data.data.deployed) { e.preventDefault(); showAlert('This app is already installed.'); window.location.reload(); return false; } } catch(err) {} return true; } async function syncTemplates() { const btn = document.getElementById('sync-btn'); const toast = document.getElementById('sync-toast'); if (!btn) return; const origText = btn.innerHTML; btn.disabled = true; btn.innerHTML = '↻ Refreshing...'; btn.classList.add('loading'); try { const resp = await fetch('/api/sync', { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (toast) { toast.textContent = data.ok ?",
|
||||
"(data.message || 'Templates refreshed') : ('Error: ' + (data.error || 'Unknown error')); toast.className = 'sync-toast ' + (data.ok ?",
|
||||
"'sync-toast-ok' : 'sync-toast-err'); toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) { setTimeout(function() { window.location.reload(); }, 1500); } } catch (err) { if (toast) { toast.textContent = 'Network error: ' + err.message; toast.className = 'sync-toast sync-toast-err'; toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } } btn.innerHTML = origText; btn.disabled = false; btn.classList.remove('loading'); } async function stackAction(event, name, action) { const btn = event.currentTarget; const origText = btn.textContent; btn.disabled = true; btn.textContent = 'In progress...'; btn.classList.add('loading'); try { const resp = await fetch('/api/stacks/' + name + '/' + action, { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (!data.ok) { showAlert('Error: ' + (data.error || 'Unknown error')); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); return; } if (action === 'update') { followUpdate(name, btn); return; } window.location.reload(); } catch (err) { showAlert('Network error: ' + err.message); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); } } function followUpdate(name, btn) { var started = Date.now(); var timer = setInterval(async function() { if (Date.now() - started > 30 * 60 * 1000) { clearInterval(timer); window.location.reload(); return; } try { var r = await fetch('/api/stacks/' + name); var d = await r.json(); if (!d.ok || !d.data) return; if (d.data.update_phase_label) btn.textContent = d.data.update_phase_label; if (!d.data.updating) { clearInterval(timer); window.location.reload(); } } catch (e) {} }, 3000); } async function deleteOrphanStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'delete-modal'; modal.innerHTML = ' Loading...",
|
||||
"'; modal.addEventListener('click', function(e) { if (e.target === modal) closeDeleteModal(); }); document.body.appendChild(modal); try { var resp = await fetch('/api/stacks/' + name + '/hdd-data'); var data = await resp.json(); var hddInfo = ''; var checkboxHTML = ''; if (data.ok && data.data && data.data.has_hdd_data) { hddInfo = ' Your data on the hard drive: '; data.data.hdd_paths.forEach(function(p) { hddInfo += ' ' + p.path + ' (' + (p.exists ?",
|
||||
"' + hddInfo + checkboxHTML + ' ' + ' Cancel ' + ' Delete ' + ' '; } catch (err) { modal.querySelector('.modal-card').innerHTML = ' Error Could not load the details: ' + err.message + ' ' + ' Close '; } } function closeDeleteModal() { var modal = document.getElementById('delete-modal'); if (modal) modal.remove(); } async function confirmDelete(name) { var btn = document.getElementById('confirm-delete-btn'); var checkbox = document.getElementById('delete-hdd-check'); var removeHDD = checkbox ?",
|
||||
"checkbox.checked : false; btn.disabled = true; btn.textContent = 'Deleting...'; try { var resp = await fetch('/api/stacks/' + name, { method: 'DELETE', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()), body: JSON.stringify({remove_hdd_data: removeHDD}) }); var data = await resp.json(); if (data.ok) { var modal = document.getElementById('delete-modal'); var removedInfo = ''; if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) { removedInfo = ' Deleted data: ' + data.data.hdd_paths_removed.join(', ') + ' '; } var preservedInfo = ''; if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) { preservedInfo = ' Kept data: ' + data.data.hdd_paths_preserved.join(', ') + ' '; } if (data.data && data.data.hdd_note) { preservedInfo += ' ' + data.data.hdd_note + ' '; } modal.querySelector('.modal-card').innerHTML = ' Deleted!",
|
||||
"' + removedInfo + preservedInfo + ' Close '; } else { showAlert('Error: ' + (data.error || 'Unknown error')); btn.disabled = false; btn.textContent = 'Delete'; } } catch (err) { showAlert('Network error: ' + err.message); btn.disabled = false; btn.textContent = 'Delete'; } } async function removeStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'remove-modal'; modal.innerHTML = ' Loading...",
|
||||
"'; modal.addEventListener('click', function(e) { if (e.target === modal) closeRemoveModal(); }); document.body.appendChild(modal); try { var [hddResp, backupResp] = await Promise.all([ fetch('/api/stacks/' + name + '/hdd-data').then(function(r) { return r.json(); }), fetch('/api/stacks/' + name + '/backup-data').then(function(r) { return r.json(); }) ]); var sections = ''; sections += ' ' + ' Always deleted: ' + ' ' + ' Docker volumes (database, app configuration) ' + ' Install configuration (app.yaml) ' + ' Secondary backup schedule ' + ' '; var hddCheckbox = ''; if (hddResp.ok && hddResp.data && hddResp.data.has_hdd_data) { var hddPaths = ''; hddResp.data.hdd_paths.forEach(function(p) { hddPaths += ' ' + p.path + ' (' + (p.exists ?",
|
||||
"Data you keep is NOT backed up automatically any more.' + ' '; hddCheckbox = '\\x3Cscript>document.getElementById(\"remove-hdd-check\").addEventListener(\"change\",function(){document.getElementById(\"remove-hdd-keep-warning\").style.display=this.checked?\"none\":\"\";}); '; } var backupCheckbox = ''; if (backupResp.ok && backupResp.data && backupResp.data.has_backups) { var bkPaths = ''; backupResp.data.backup_paths.forEach(function(p) { if (p.exists) bkPaths += ' ' + p.path + ' (' + p.size_human + ') '; }); if (bkPaths) { sections += ' ' + ' Backup data: ' + bkPaths + ' Delete backup data ' + ' ' + 'The nightly restic snapshots cannot be deleted one by one — they expire automatically under the retention rules.' + ' '; } } modal.querySelector('.modal-card').innerHTML = ' Remove app: ' + name + ' ' + ' The app goes back to \"Not installed\"."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
21:40:47 ==== ENGINE EDGE docmost: postgres:16-alpine -> postgres:17-alpine (app image does NOT move)
|
||||
21:40:47 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
|
||||
21:42:23 [1] deployed, controller state=running, pinned={'docmost': 'docmost/docmost:0.96.0', 'docmost-postgres': 'postgres:16-alpine', 'docmost-redis': 'redis:7-alpine'}
|
||||
21:42:23 [2] seeding through the app's own route
|
||||
21:42:23 docmost: /api/auth/setup http=200 rc=0
|
||||
21:42:23 [3] control C1 — reading the seed back BEFORE the engine moves
|
||||
21:42:24 docmost: login as the seeded user http=200 ok=True
|
||||
21:42:29 [3b] engine observables BEFORE written to engine-before.txt
|
||||
21:42:29 [4] „Mentés most" -> 200 {'ok': True, 'message': 'Mentés elindítva'}
|
||||
21:44:09 [4] backup idle; last=None
|
||||
21:44:10 [5] drill commit a26b546a99b8: docmost postgres:16-alpine -> postgres:17-alpine (push rc=0)
|
||||
21:44:15 [5] badge HU: [{'title': 'Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.', 'text': 'Frissítés elérhető — ma'}]
|
||||
21:44:15 [5] badge EN: [{'title': 'A newer version of this app is available. Select the Update button to start it.', 'text': 'Update available — today'}]
|
||||
21:44:15 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}
|
||||
21:44:15 + 0.0s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None
|
||||
21:44:16 + 1.1s phase=pulling label=Új verzió letöltése… err=None hold=None
|
||||
21:44:17 + 2.1s phase=starting label=Indítás az új verzióval… err=None hold=None
|
||||
21:44:20 + 5.1s phase=failed label=A frissítés nem sikerült err=A(z) docmost frissítése 2026-09-21 21:44-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:44 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza. hold=A(z) docmost frissítése 2026-09-21 21:44-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:44 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.
|
||||
21:44:23 [6b] engine observables AFTER written to engine-after.txt
|
||||
21:44:23 [6c] hold sentences HU: ['Docmost — Felhom.eu document.documentElement.className=document.documentElement.className.replace(\'no-js\',\'js\'); function csrfHeaders(){var el=document.querySelector(\'meta[name="csrf-token"]\');return el?{\'X-CSRF-Token\':el.content}:{};} function felhomConfirm(el,question,onYes){ if(!el||el.dataset.fcOpen)return; el.dataset.fcOpen=\'1\'; var wrap=document.createElement(\'span\');wrap.className=\'inline-confirm\'; var q=document.createElement(\'span\');q.className=\'inline-confirm-q\';q.textContent=question; var yes=document.createElement(\'button\');yes.type=\'button\';yes.className=\'btn btn-xs btn-danger\';yes.textContent=\'Igen\'; var no=document.createElement(\'button\');no.type=\'button\';no.className=\'btn btn-xs btn-outline\';no.textContent=\'Mégse\'; wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no); el.style.display=\'none\';el.parentNode.insertBefore(wrap,el.nextSibling); function close(){wrap.remove();el.style.display=\'\';delete el.dataset.fcOpen;} no.addEventListener(\'click\',close); yes.addEventListener(\'click\',function(){close();onYes();}); } document.addEventListener(\'click\',function(e){ var btn=e.target.closest?e.target.closest(\'[data-confirm]\'):null; if(!btn)return; e.preventDefault(); felhomConfirm(btn,btn.getAttribute(\'data-confirm\'),function(){ var form=btn.closest(\'form\'); if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();} }); }); document.addEventListener(\'click\',function(e){ var btn=e.target.closest?e.target.closest(\'.nav-group-toggle\'):null; if(!btn)return; var group=btn.closest(\'.nav-group\'); if(!group)return; var willOpen=!group.classList.contains(\'is-open\'); document.querySelectorAll(\'.nav-group.is-open\').forEach(function(g){ g.classList.remove(\'is-open\'); var t=g.querySelector(\'.nav-group-toggle\'); if(t)t.setAttribute(\'aria-expanded\',\'false\'); }); if(willOpen){group.classList.add(\'is-open\');btn.setAttribute(\'aria-expanded\',\'true\');} }); function showAlert(msg){var o=document.createElement(\'div\');o.className=\'modal-overlay\';o.id=\'alert-modal\';o.addEventListener(\'click\',function(e){if(e.target===o)o.remove();});var c=document.createElement(\'div\');c.className=\'modal-card\';c.innerHTML=\' Üzenet \'+msg.replace(/&/g,\'&\').replace(/ /g,\'>\')+\' OK \';o.appendChild(c);document.body.appendChild(o);} Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások Docmost Leállítva Frissítés elérhető — ma Megnyitás ↗ Napló Exportálás Beállítások A(z) docmost frissítése 2026-09-21 21:44-kor nem sikerült, és az alkalmazás nem indult el az új verzióval.', 'Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:44 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.', 'Mentések Modern wiki és dokumentáció platform Notion-szerű szerkesztővel ~200M RAM productivity Csak x86 Áthelyezés másik tárhelyre Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át.']
|
||||
21:44:23 [6c] hold sentences EN: ['Docmost — Felhom.eu document.documentElement.className=document.documentElement.className.replace(\'no-js\',\'js\'); function csrfHeaders(){var el=document.querySelector(\'meta[name="csrf-token"]\');return el?{\'X-CSRF-Token\':el.content}:{};} function felhomConfirm(el,question,onYes){ if(!el||el.dataset.fcOpen)return; el.dataset.fcOpen=\'1\'; var wrap=document.createElement(\'span\');wrap.className=\'inline-confirm\'; var q=document.createElement(\'span\');q.className=\'inline-confirm-q\';q.textContent=question; var yes=document.createElement(\'button\');yes.type=\'button\';yes.className=\'btn btn-xs btn-danger\';yes.textContent=\'Yes\'; var no=document.createElement(\'button\');no.type=\'button\';no.className=\'btn btn-xs btn-outline\';no.textContent=\'Cancel\'; wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no); el.style.display=\'none\';el.parentNode.insertBefore(wrap,el.nextSibling); function close(){wrap.remove();el.style.display=\'\';delete el.dataset.fcOpen;} no.addEventListener(\'click\',close); yes.addEventListener(\'click\',function(){close();onYes();}); } document.addEventListener(\'click\',function(e){ var btn=e.target.closest?e.target.closest(\'[data-confirm]\'):null; if(!btn)return; e.preventDefault(); felhomConfirm(btn,btn.getAttribute(\'data-confirm\'),function(){ var form=btn.closest(\'form\'); if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();} }); }); document.addEventListener(\'click\',function(e){ var btn=e.target.closest?e.target.closest(\'.nav-group-toggle\'):null; if(!btn)return; var group=btn.closest(\'.nav-group\'); if(!group)return; var willOpen=!group.classList.contains(\'is-open\'); document.querySelectorAll(\'.nav-group.is-open\').forEach(function(g){ g.classList.remove(\'is-open\'); var t=g.querySelector(\'.nav-group-toggle\'); if(t)t.setAttribute(\'aria-expanded\',\'false\'); }); if(willOpen){group.classList.add(\'is-open\');btn.setAttribute(\'aria-expanded\',\'true\');} }); function showAlert(msg){var o=document.createElement(\'div\');o.className=\'modal-overlay\';o.id=\'alert-modal\';o.addEventListener(\'click\',function(e){if(e.target===o)o.remove();});var c=document.createElement(\'div\');c.className=\'modal-card\';c.innerHTML=\' Message \'+msg.replace(/&/g,\'&\').replace(/ /g,\'>\')+\' OK \';o.appendChild(c);document.body.appendChild(o);} Launcher Dashboard Apps Storage Drives Network storage Backup Overview Remote backup Apps Restore Sharing Network sharing System monitor Debug Settings System Notifications Security and access 0.261.0 Magyar English Sign out ↗ The hub connection is off — central monitoring is not running System monitor → ← Apps Docmost Stopped Update available — today Open ↗ Log Export Settings A(z) docmost frissítése 2026-09-21 21:44-kor nem sikerült, és az alkalmazás nem indult el az új verzióval.', 'Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:44 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.', 'Backups A modern wiki and documentation platform with a Notion-like editor ~200M RAM productivity x86 only Move to another storage You can move this app’s data to another connected storage.']
|
||||
21:44:23 [7] reading the seed back AFTER the engine moved
|
||||
21:44:23 docmost: login as the seeded user http=404 ok=False
|
||||
21:44:23 docmost: login body 404 page not found
|
||||
|
||||
21:44:25 [8] pinned = {'docmost': 'docmost/docmost:0.96.0', 'docmost-postgres': 'postgres:17-alpine', 'docmost-redis': 'redis:7-alpine'}
|
||||
21:44:25 [8] installed = {'docmost': 'docmost/docmost:0.96.0', 'docmost-postgres': 'postgres:16-alpine', 'docmost-redis': 'redis:7-alpine'}
|
||||
21:44:25 [8] compose = ['image: docmost/docmost:0.96.0', 'image: postgres:17-alpine', 'image: redis:7-alpine']
|
||||
21:44:25 [8] inspect = []
|
||||
21:44:25 [W] the edge ended badly — walking the household's way out (restore)
|
||||
21:44:25 [R] restoring docmost from snapshot 'helyi' (of 1 offered)
|
||||
21:44:25 [R] POST /backup/restore -> HTTP/2 302 ['location: /backups/restore?flash=flash.restore.started']
|
||||
21:44:25 + 0.0s restore (True, None, None)
|
||||
21:44:56 + 30.4s restore (False, None, None)
|
||||
21:44:56 [R] after restore: state=running hold=None phase=None
|
||||
21:44:59 docmost: login as the seeded user http=200 ok=True
|
||||
21:44:59 [W] the seed read back after the restore: True
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"pinned_images": {
|
||||
"docmost": "docmost/docmost:0.96.0",
|
||||
"docmost-postgres": "postgres:17-alpine",
|
||||
"docmost-redis": "redis:7-alpine"
|
||||
},
|
||||
"installed_images": {
|
||||
"docmost": "docmost/docmost:0.96.0",
|
||||
"docmost-postgres": "postgres:16-alpine",
|
||||
"docmost-redis": "redis:7-alpine"
|
||||
},
|
||||
"catalog_images": {
|
||||
"docmost": "docmost/docmost:0.96.0",
|
||||
"docmost-postgres": "postgres:17-alpine",
|
||||
"docmost-redis": "redis:7-alpine"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: docmost/docmost:0.96.0",
|
||||
"image: postgres:17-alpine",
|
||||
"image: redis:7-alpine"
|
||||
],
|
||||
"docker_inspect": []
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"accepted": true,
|
||||
"http": "202",
|
||||
"phases": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"phase": "safety-dump",
|
||||
"label": "Adatbázis pillanatkép…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 1.1,
|
||||
"phase": "pulling",
|
||||
"label": "Új verzió letöltése…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 2.1,
|
||||
"phase": "starting",
|
||||
"label": "Indítás az új verzióval…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 5.1,
|
||||
"phase": "failed",
|
||||
"label": "A frissítés nem sikerült",
|
||||
"updating": false,
|
||||
"error": "A(z) docmost frissítése 2026-09-21 21:44-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:44 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"hold": "A(z) docmost frissítése 2026-09-21 21:44-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:44 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza."
|
||||
}
|
||||
],
|
||||
"duration_s": 5.2,
|
||||
"final_phase": "failed",
|
||||
"update_error": "A(z) docmost frissítése 2026-09-21 21:44-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:44 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"hold_reason": "A(z) docmost frissítése 2026-09-21 21:44-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:44 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"state": "stopped"
|
||||
}
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
{
|
||||
"harness_version": 1,
|
||||
"app": "docmost",
|
||||
"leg": "engine-major-postgres",
|
||||
"venue": "guest 9202 demo-hp-scratch, controller 0.261.0",
|
||||
"from": {
|
||||
"docmost-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"to": {
|
||||
"docmost-postgres": "postgres:17-alpine"
|
||||
},
|
||||
"verdict": "failed",
|
||||
"seed_read_before": true,
|
||||
"seed_read_after": false,
|
||||
"healthy_after": true,
|
||||
"migration_observed": null,
|
||||
"abort": "not-attempted",
|
||||
"abort_detail": null,
|
||||
"engine_state_before": "=== [1] PG_VERSION — the datadir's own record of its major\n16\nUnable to find image 'alpine:3.20' locally\n3.20: Pulling from library/alpine\n25f1d6b1951a: Already exists\nDigest: sha256:d9e853e87e55526f6b2917df91a2115c36dd7c696a35be12163d44e6e2a4b6bc\nStatus: Downloaded newer image for alpine:3.20\n16\n=== [2] the engine's REFUSAL, verbatim (the whole point of this leg)\n done\nserver started\nCREATE DATABASE\n\n\n/usr/local/bin/docker-entrypoint.sh: ignoring /docker-entrypoint-initdb.d/*\n\nwaiting for server to shut down....2026-09-21 21:42:03.450 CEST [41] LOG: received fast shutdown request\n2026-09-21 21:42:03.453 CEST [41] LOG: aborting any active transactions\n2026-09-21 21:42:03.456 CEST [41] LOG: background worker \"logical replication launcher\" (PID 47) exited with exit code 1\n2026-09-21 21:42:03.457 CEST [42] LOG: shutting down\n2026-09-21 21:42:03.459 CEST [42] LOG: checkpoint starting: shutdown immediate\n2026-09-21 21:42:03.544 CEST [42] LOG: checkpoint complete: wrote 926 buffers (5.7%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.022 s, sync=0.051 s, total=0.088 s; sync files=301, longest=0.022 s, average=0.001 s; distance=4283 kB, estimate=4283 kB; lsn=0/1925D58, redo lsn=0/1925D58\n2026-09-21 21:42:03.558 CEST [41] LOG: database system is shut down\n done\nserver stopped\n\nPostgreSQL init process complete; ready for start up.\n\n2026-09-21 21:42:03.687 CEST [1] LOG: starting PostgreSQL 16.15 on x86_64-pc-linux-musl, compiled by gcc (Alpine 15.2.0) 15.2.0, 64-bit\n2026-09-21 21:42:03.687 CEST [1] LOG: listening on IPv4 address \"0.0.0.0\", port 5432\n2026-09-21 21:42:03.687 CEST [1] LOG: listening on IPv6 address \"::\", port 5432\n2026-09-21 21:42:03.692 CEST [1] LOG: listening on Unix socket \"/var/run/postgresql/.s.PGSQL.5432\"\n2026-09-21 21:42:03.701 CEST [57] LOG: database system was shut down at 2026-09-21 21:42:03 CEST\n2026-09-21 21:42:03.710 CEST [1] LOG: database system is ready to accept connections\n=== [3] container state\nimage=postgres:16-alpine running=true restarts=0 exit=0\n=== [4] the version binary in the image\npostgres (PostgreSQL) 16.15\n",
|
||||
"engine_state_after": "=== [1] PG_VERSION — the datadir's own record of its major\nError response from daemon: No such container: docmost-postgres\ndocker: invalid spec: :/d: empty section between colons\n\nRun 'docker run --help' for more information\n=== [2] the engine's REFUSAL, verbatim (the whole point of this leg)\nError response from daemon: No such container: docmost-postgres\n=== [3] container state\n\nerror: no such object: docmost-postgres\n=== [4] the version binary in the image\nError response from daemon: No such container: docmost-postgres\n(cannot exec — not running)\n",
|
||||
"duration_s": 254.5,
|
||||
"measured_at": "2026-09-21T19:40:47.708577+00:00",
|
||||
"evidence": "apps/docmost-engine-postgres/",
|
||||
"notes": [
|
||||
"ended HELD or failed — a RESULT, not an error of the run"
|
||||
],
|
||||
"badge_catchup_seconds": 4.5,
|
||||
"phases": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"phase": "safety-dump",
|
||||
"label": "Adatbázis pillanatkép…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 1.1,
|
||||
"phase": "pulling",
|
||||
"label": "Új verzió letöltése…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 2.1,
|
||||
"phase": "starting",
|
||||
"label": "Indítás az új verzióval…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 5.1,
|
||||
"phase": "failed",
|
||||
"label": "A frissítés nem sikerült",
|
||||
"updating": false,
|
||||
"error": "A(z) docmost frissítése 2026-09-21 21:44-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:44 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"hold": "A(z) docmost frissítése 2026-09-21 21:44-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:44 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza."
|
||||
}
|
||||
],
|
||||
"final_phase": "failed",
|
||||
"hold_reason": "A(z) docmost frissítése 2026-09-21 21:44-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:44 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"update_error": "A(z) docmost frissítése 2026-09-21 21:44-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:44 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"hold_sentences": {
|
||||
"hu": [
|
||||
"Docmost — Felhom.eu document.documentElement.className=document.documentElement.className.replace('no-js','js'); function csrfHeaders(){var el=document.querySelector('meta[name=\"csrf-token\"]');return el?{'X-CSRF-Token':el.content}:{};} function felhomConfirm(el,question,onYes){ if(!el||el.dataset.fcOpen)return; el.dataset.fcOpen='1'; var wrap=document.createElement('span');wrap.className='inline-confirm'; var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question; var yes=document.createElement('button');yes.type='button';yes.className='btn btn-xs btn-danger';yes.textContent='Igen'; var no=document.createElement('button');no.type='button';no.className='btn btn-xs btn-outline';no.textContent='Mégse'; wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no); el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling); function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;} no.addEventListener('click',close); yes.addEventListener('click',function(){close();onYes();}); } document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('[data-confirm]'):null; if(!btn)return; e.preventDefault(); felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){ var form=btn.closest('form'); if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();} }); }); document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null; if(!btn)return; var group=btn.closest('.nav-group'); if(!group)return; var willOpen=!group.classList.contains('is-open'); document.querySelectorAll('.nav-group.is-open').forEach(function(g){ g.classList.remove('is-open'); var t=g.querySelector('.nav-group-toggle'); if(t)t.setAttribute('aria-expanded','false'); }); if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');} }); function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML=' Üzenet '+msg.replace(/&/g,'&').replace(/ /g,'>')+' OK ';o.appendChild(c);document.body.appendChild(o);} Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások Docmost Leállítva Frissítés elérhető — ma Megnyitás ↗ Napló Exportálás Beállítások A(z) docmost frissítése 2026-09-21 21:44-kor nem sikerült, és az alkalmazás nem indult el az új verzióval.",
|
||||
"Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:44 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"Mentések Modern wiki és dokumentáció platform Notion-szerű szerkesztővel ~200M RAM productivity Csak x86 Áthelyezés másik tárhelyre Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át.",
|
||||
"Válassz céltárhelyet… SCRATCH HDD (/mnt/felhom-drives/scratch_hdd) Áthelyezés function appMigFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; } function appMigRender(job){ var names={stop:'Leállítás',copy:'Adatok másolása',verify:'Ellenőrzés',flip:'Újratelepítés',redeploy:'Újratelepítés',cleanup:'Régi adatok törlése'}; var s=names[job.phase]||job.phase; if(job.phase==='copy'&&job.bytes_total>0){ s+=' ('+Math.floor(100*job.bytes_done/job.bytes_total)+'% — '+appMigFmtGB(job.bytes_done)+'/'+appMigFmtGB(job.bytes_total)+')'; } return s+'…'; } function appMigWatch(){ var panel=document.getElementById('app-migrate-progress'); if(panel) panel.style.display='block'; function tick(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ var job=d.data&&d.data.job; if(!job){ if(panel) panel.textContent='Nincs folyamatban áthelyezés.'; return; } if(panel) panel.innerHTML=appMigRender(job); if(job.phase==='done'){ if(panel) panel.innerHTML+=' Kész '; setTimeout(function(){location.reload();},1500); return; } if(job.phase==='aborted'){ if(panel) panel.innerHTML+=' Megszakadt: '+(job.error||'')+' A régi adatok érintetlenek.'; return; } setTimeout(tick,1500); }).catch(function(){ setTimeout(tick,2000); }); } tick(); } function appMigrate(btn,app,label){ var sel=document.getElementById('app-migrate-target'); var target=sel?sel.value:''; if(!target){ showAlert('Válassz céltárhelyet.'); return; } felhomConfirm(btn,'Áthelyezed a(z) '+label+' adatait ide: '+target+'?",
|
||||
"A régi adatok csak sikeres áthelyezés után törlődnek.',function(){ fetch('/api/storage/migrate-app',{method:'POST',headers:Object.assign({'Content-Type':'application/json'},csrfHeaders()),body:JSON.stringify({app:app,target:target})}) .then(function(r){return r.json();}).then(function(d){ if(d.ok){ appMigWatch(); } else { showAlert('Hiba: '+(d.error||'ismeretlen')); } }) .catch(function(e){ showAlert('Hiba: '+e); }); }); } (function(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ if(d.data&&d.data.job){ appMigWatch(); } }).catch(function(){}); })(); Mire használható?",
|
||||
"Csapat tudásbázis és belső dokumentáció Notion-szerű blokk szerkesztő valós idejű együttműködéssel Terek és oldalak hierarchikus szervezése Képek, táblázatok és kód blokkok beágyazása Keresés a teljes dokumentációban Első lépések Nyisd meg a docs.DOMAIN címet a böngészőben Az első regisztrált felhasználó automatikusan admin lesz Hozd létre az első teret (Space) a dokumentumoknak Kezdj el írni a Notion-szerű szerkesztőben Előfeltételek x86 processzor szükséges Legalább 768 MB szabad RAM (Docmost + PostgreSQL + Redis) Dokumentáció Hivatalos dokumentáció ↗ (function(){ var burger=document.querySelector('.nav-burger'); var sidebar=document.getElementById('sidebar'); var backdrop=document.querySelector('.nav-backdrop'); if(!burger||!sidebar||!backdrop)return; function setOpen(open){ sidebar.classList.toggle('is-open',open); document.body.classList.toggle('nav-open',open); backdrop.hidden=!open; burger.setAttribute('aria-expanded',open?'true':'false'); } burger.addEventListener('click',function(){setOpen(!sidebar.classList.contains('is-open'));}); backdrop.addEventListener('click',function(){setOpen(false);}); document.addEventListener('keydown',function(e){ if(e.key==='Escape'&&sidebar.classList.contains('is-open'))setOpen(false); }); })(); document.addEventListener('click', function(e) { if (e.target.closest('a, button, .btn, input, select, textarea, .app-row-actions, .stack-detail-actions')) return; var card = e.target.closest('[data-href]'); if (card) window.location.href = card.dataset.href; }); async function checkBeforeDeploy(e, name) { try { var resp = await fetch('/api/stacks/' + name); var data = await resp.json(); if (data.ok && data.data && data.data.deployed) { e.preventDefault(); showAlert('Ez az alkalmazás már telepítve van.'); window.location.reload(); return false; } } catch(err) {} return true; } async function syncTemplates() { const btn = document.getElementById('sync-btn'); const toast = document.getElementById('sync-toast'); if (!btn) return; const origText = btn.innerHTML; btn.disabled = true; btn.innerHTML = '↻ Frissítés...'; btn.classList.add('loading'); try { const resp = await fetch('/api/sync', { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (toast) { toast.textContent = data.ok ?",
|
||||
"(data.message || 'Sablonok frissítve') : ('Hiba: ' + (data.error || 'Ismeretlen hiba')); toast.className = 'sync-toast ' + (data.ok ?",
|
||||
"'sync-toast-ok' : 'sync-toast-err'); toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) { setTimeout(function() { window.location.reload(); }, 1500); } } catch (err) { if (toast) { toast.textContent = 'Hálózati hiba: ' + err.message; toast.className = 'sync-toast sync-toast-err'; toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } } btn.innerHTML = origText; btn.disabled = false; btn.classList.remove('loading'); } async function stackAction(event, name, action) { const btn = event.currentTarget; const origText = btn.textContent; btn.disabled = true; btn.textContent = 'Folyamatban...'; btn.classList.add('loading'); try { const resp = await fetch('/api/stacks/' + name + '/' + action, { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (!data.ok) { showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba')); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); return; } if (action === 'update') { followUpdate(name, btn); return; } window.location.reload(); } catch (err) { showAlert('Hálózati hiba: ' + err.message); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); } } function followUpdate(name, btn) { var started = Date.now(); var timer = setInterval(async function() { if (Date.now() - started > 30 * 60 * 1000) { clearInterval(timer); window.location.reload(); return; } try { var r = await fetch('/api/stacks/' + name); var d = await r.json(); if (!d.ok || !d.data) return; if (d.data.update_phase_label) btn.textContent = d.data.update_phase_label; if (!d.data.updating) { clearInterval(timer); window.location.reload(); } } catch (e) {} }, 3000); } async function deleteOrphanStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'delete-modal'; modal.innerHTML = ' Betöltés...",
|
||||
"'; modal.addEventListener('click', function(e) { if (e.target === modal) closeDeleteModal(); }); document.body.appendChild(modal); try { var resp = await fetch('/api/stacks/' + name + '/hdd-data'); var data = await resp.json(); var hddInfo = ''; var checkboxHTML = ''; if (data.ok && data.data && data.data.has_hdd_data) { hddInfo = ' Felhasználói adatok a merevlemezen: '; data.data.hdd_paths.forEach(function(p) { hddInfo += ' ' + p.path + ' (' + (p.exists ?",
|
||||
"' + ' Ez a művelet nem visszavonható!",
|
||||
"' + hddInfo + checkboxHTML + ' ' + ' Mégsem ' + ' Törlés ' + ' '; } catch (err) { modal.querySelector('.modal-card').innerHTML = ' Hiba Nem sikerült lekérni az adatokat: ' + err.message + ' ' + ' Bezárás '; } } function closeDeleteModal() { var modal = document.getElementById('delete-modal'); if (modal) modal.remove(); } async function confirmDelete(name) { var btn = document.getElementById('confirm-delete-btn'); var checkbox = document.getElementById('delete-hdd-check'); var removeHDD = checkbox ?",
|
||||
"checkbox.checked : false; btn.disabled = true; btn.textContent = 'Törlés folyamatban...'; try { var resp = await fetch('/api/stacks/' + name, { method: 'DELETE', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()), body: JSON.stringify({remove_hdd_data: removeHDD}) }); var data = await resp.json(); if (data.ok) { var modal = document.getElementById('delete-modal'); var removedInfo = ''; if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) { removedInfo = ' Törölt adatok: ' + data.data.hdd_paths_removed.join(', ') + ' '; } var preservedInfo = ''; if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) { preservedInfo = ' Megőrzött adatok: ' + data.data.hdd_paths_preserved.join(', ') + ' '; } if (data.data && data.data.hdd_note) { preservedInfo += ' ' + data.data.hdd_note + ' '; } modal.querySelector('.modal-card').innerHTML = ' Sikeresen törölve!",
|
||||
"' + removedInfo + preservedInfo + ' Bezárás '; } else { showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba')); btn.disabled = false; btn.textContent = 'Törlés'; } } catch (err) { showAlert('Hálózati hiba: ' + err.message); btn.disabled = false; btn.textContent = 'Törlés'; } } async function removeStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'remove-modal'; modal.innerHTML = ' Betöltés...",
|
||||
"'; modal.addEventListener('click', function(e) { if (e.target === modal) closeRemoveModal(); }); document.body.appendChild(modal); try { var [hddResp, backupResp] = await Promise.all([ fetch('/api/stacks/' + name + '/hdd-data').then(function(r) { return r.json(); }), fetch('/api/stacks/' + name + '/backup-data').then(function(r) { return r.json(); }) ]); var sections = ''; sections += ' ' + ' Mindig törlődik: ' + ' ' + ' Docker kötetek (adatbázis, alkalmazás konfiguráció) ' + ' Telepítési konfiguráció (app.yaml) ' + ' Másodlagos mentés ütemezése ' + ' '; var hddCheckbox = ''; if (hddResp.ok && hddResp.data && hddResp.data.has_hdd_data) { var hddPaths = ''; hddResp.data.hdd_paths.forEach(function(p) { hddPaths += ' ' + p.path + ' (' + (p.exists ?"
|
||||
],
|
||||
"en": [
|
||||
"Docmost — Felhom.eu document.documentElement.className=document.documentElement.className.replace('no-js','js'); function csrfHeaders(){var el=document.querySelector('meta[name=\"csrf-token\"]');return el?{'X-CSRF-Token':el.content}:{};} function felhomConfirm(el,question,onYes){ if(!el||el.dataset.fcOpen)return; el.dataset.fcOpen='1'; var wrap=document.createElement('span');wrap.className='inline-confirm'; var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question; var yes=document.createElement('button');yes.type='button';yes.className='btn btn-xs btn-danger';yes.textContent='Yes'; var no=document.createElement('button');no.type='button';no.className='btn btn-xs btn-outline';no.textContent='Cancel'; wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no); el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling); function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;} no.addEventListener('click',close); yes.addEventListener('click',function(){close();onYes();}); } document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('[data-confirm]'):null; if(!btn)return; e.preventDefault(); felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){ var form=btn.closest('form'); if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();} }); }); document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null; if(!btn)return; var group=btn.closest('.nav-group'); if(!group)return; var willOpen=!group.classList.contains('is-open'); document.querySelectorAll('.nav-group.is-open').forEach(function(g){ g.classList.remove('is-open'); var t=g.querySelector('.nav-group-toggle'); if(t)t.setAttribute('aria-expanded','false'); }); if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');} }); function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML=' Message '+msg.replace(/&/g,'&').replace(/ /g,'>')+' OK ';o.appendChild(c);document.body.appendChild(o);} Launcher Dashboard Apps Storage Drives Network storage Backup Overview Remote backup Apps Restore Sharing Network sharing System monitor Debug Settings System Notifications Security and access 0.261.0 Magyar English Sign out ↗ The hub connection is off — central monitoring is not running System monitor → ← Apps Docmost Stopped Update available — today Open ↗ Log Export Settings A(z) docmost frissítése 2026-09-21 21:44-kor nem sikerült, és az alkalmazás nem indult el az új verzióval.",
|
||||
"Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:44 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"Backups A modern wiki and documentation platform with a Notion-like editor ~200M RAM productivity x86 only Move to another storage You can move this app’s data to another connected storage.",
|
||||
"Choose a target storage… SCRATCH HDD (/mnt/felhom-drives/scratch_hdd) Move function appMigFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; } function appMigRender(job){ var names={stop:'Stopping',copy:'Copying data',verify:'Checking',flip:'Reinstalling',redeploy:'Reinstalling',cleanup:'Deleting old data'}; var s=names[job.phase]||job.phase; if(job.phase==='copy'&&job.bytes_total>0){ s+=' ('+Math.floor(100*job.bytes_done/job.bytes_total)+'% — '+appMigFmtGB(job.bytes_done)+'/'+appMigFmtGB(job.bytes_total)+')'; } return s+'…'; } function appMigWatch(){ var panel=document.getElementById('app-migrate-progress'); if(panel) panel.style.display='block'; function tick(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ var job=d.data&&d.data.job; if(!job){ if(panel) panel.textContent='No move in progress.'; return; } if(panel) panel.innerHTML=appMigRender(job); if(job.phase==='done'){ if(panel) panel.innerHTML+=' Done '; setTimeout(function(){location.reload();},1500); return; } if(job.phase==='aborted'){ if(panel) panel.innerHTML+=' Stopped: '+(job.error||'')+' The old data is untouched.'; return; } setTimeout(tick,1500); }).catch(function(){ setTimeout(tick,2000); }); } tick(); } function appMigrate(btn,app,label){ var sel=document.getElementById('app-migrate-target'); var target=sel?sel.value:''; if(!target){ showAlert('Choose a target storage.'); return; } felhomConfirm(btn,'Move the '+label+' data to: '+target+'?",
|
||||
"The old data is deleted only after a successful move.',function(){ fetch('/api/storage/migrate-app',{method:'POST',headers:Object.assign({'Content-Type':'application/json'},csrfHeaders()),body:JSON.stringify({app:app,target:target})}) .then(function(r){return r.json();}).then(function(d){ if(d.ok){ appMigWatch(); } else { showAlert('Error: '+(d.error||'unknown')); } }) .catch(function(e){ showAlert('Error: '+e); }); }); } (function(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ if(d.data&&d.data.job){ appMigWatch(); } }).catch(function(){}); })(); What is it for?",
|
||||
"A knowledge base and internal documentation for a team A Notion-like block editor several people can use at once Spaces and pages, arranged in a tree Embed images, tables and code blocks Search across all of it First steps Open docs.DOMAIN in your browser The first person to sign up becomes the admin Create your first Space for the documents Start writing in the Notion-like editor Before you start An x86 processor is needed At least 768 MB of free RAM (Docmost + PostgreSQL + Redis) Documentation Official documentation ↗ (function(){ var burger=document.querySelector('.nav-burger'); var sidebar=document.getElementById('sidebar'); var backdrop=document.querySelector('.nav-backdrop'); if(!burger||!sidebar||!backdrop)return; function setOpen(open){ sidebar.classList.toggle('is-open',open); document.body.classList.toggle('nav-open',open); backdrop.hidden=!open; burger.setAttribute('aria-expanded',open?'true':'false'); } burger.addEventListener('click',function(){setOpen(!sidebar.classList.contains('is-open'));}); backdrop.addEventListener('click',function(){setOpen(false);}); document.addEventListener('keydown',function(e){ if(e.key==='Escape'&&sidebar.classList.contains('is-open'))setOpen(false); }); })(); document.addEventListener('click', function(e) { if (e.target.closest('a, button, .btn, input, select, textarea, .app-row-actions, .stack-detail-actions')) return; var card = e.target.closest('[data-href]'); if (card) window.location.href = card.dataset.href; }); async function checkBeforeDeploy(e, name) { try { var resp = await fetch('/api/stacks/' + name); var data = await resp.json(); if (data.ok && data.data && data.data.deployed) { e.preventDefault(); showAlert('This app is already installed.'); window.location.reload(); return false; } } catch(err) {} return true; } async function syncTemplates() { const btn = document.getElementById('sync-btn'); const toast = document.getElementById('sync-toast'); if (!btn) return; const origText = btn.innerHTML; btn.disabled = true; btn.innerHTML = '↻ Refreshing...'; btn.classList.add('loading'); try { const resp = await fetch('/api/sync', { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (toast) { toast.textContent = data.ok ?",
|
||||
"(data.message || 'Templates refreshed') : ('Error: ' + (data.error || 'Unknown error')); toast.className = 'sync-toast ' + (data.ok ?",
|
||||
"'sync-toast-ok' : 'sync-toast-err'); toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) { setTimeout(function() { window.location.reload(); }, 1500); } } catch (err) { if (toast) { toast.textContent = 'Network error: ' + err.message; toast.className = 'sync-toast sync-toast-err'; toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } } btn.innerHTML = origText; btn.disabled = false; btn.classList.remove('loading'); } async function stackAction(event, name, action) { const btn = event.currentTarget; const origText = btn.textContent; btn.disabled = true; btn.textContent = 'In progress...'; btn.classList.add('loading'); try { const resp = await fetch('/api/stacks/' + name + '/' + action, { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (!data.ok) { showAlert('Error: ' + (data.error || 'Unknown error')); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); return; } if (action === 'update') { followUpdate(name, btn); return; } window.location.reload(); } catch (err) { showAlert('Network error: ' + err.message); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); } } function followUpdate(name, btn) { var started = Date.now(); var timer = setInterval(async function() { if (Date.now() - started > 30 * 60 * 1000) { clearInterval(timer); window.location.reload(); return; } try { var r = await fetch('/api/stacks/' + name); var d = await r.json(); if (!d.ok || !d.data) return; if (d.data.update_phase_label) btn.textContent = d.data.update_phase_label; if (!d.data.updating) { clearInterval(timer); window.location.reload(); } } catch (e) {} }, 3000); } async function deleteOrphanStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'delete-modal'; modal.innerHTML = ' Loading...",
|
||||
"'; modal.addEventListener('click', function(e) { if (e.target === modal) closeDeleteModal(); }); document.body.appendChild(modal); try { var resp = await fetch('/api/stacks/' + name + '/hdd-data'); var data = await resp.json(); var hddInfo = ''; var checkboxHTML = ''; if (data.ok && data.data && data.data.has_hdd_data) { hddInfo = ' Your data on the hard drive: '; data.data.hdd_paths.forEach(function(p) { hddInfo += ' ' + p.path + ' (' + (p.exists ?",
|
||||
"' + hddInfo + checkboxHTML + ' ' + ' Cancel ' + ' Delete ' + ' '; } catch (err) { modal.querySelector('.modal-card').innerHTML = ' Error Could not load the details: ' + err.message + ' ' + ' Close '; } } function closeDeleteModal() { var modal = document.getElementById('delete-modal'); if (modal) modal.remove(); } async function confirmDelete(name) { var btn = document.getElementById('confirm-delete-btn'); var checkbox = document.getElementById('delete-hdd-check'); var removeHDD = checkbox ?",
|
||||
"checkbox.checked : false; btn.disabled = true; btn.textContent = 'Deleting...'; try { var resp = await fetch('/api/stacks/' + name, { method: 'DELETE', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()), body: JSON.stringify({remove_hdd_data: removeHDD}) }); var data = await resp.json(); if (data.ok) { var modal = document.getElementById('delete-modal'); var removedInfo = ''; if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) { removedInfo = ' Deleted data: ' + data.data.hdd_paths_removed.join(', ') + ' '; } var preservedInfo = ''; if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) { preservedInfo = ' Kept data: ' + data.data.hdd_paths_preserved.join(', ') + ' '; } if (data.data && data.data.hdd_note) { preservedInfo += ' ' + data.data.hdd_note + ' '; } modal.querySelector('.modal-card').innerHTML = ' Deleted!",
|
||||
"' + removedInfo + preservedInfo + ' Close '; } else { showAlert('Error: ' + (data.error || 'Unknown error')); btn.disabled = false; btn.textContent = 'Delete'; } } catch (err) { showAlert('Network error: ' + err.message); btn.disabled = false; btn.textContent = 'Delete'; } } async function removeStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'remove-modal'; modal.innerHTML = ' Loading...",
|
||||
"'; modal.addEventListener('click', function(e) { if (e.target === modal) closeRemoveModal(); }); document.body.appendChild(modal); try { var [hddResp, backupResp] = await Promise.all([ fetch('/api/stacks/' + name + '/hdd-data').then(function(r) { return r.json(); }), fetch('/api/stacks/' + name + '/backup-data').then(function(r) { return r.json(); }) ]); var sections = ''; sections += ' ' + ' Always deleted: ' + ' ' + ' Docker volumes (database, app configuration) ' + ' Install configuration (app.yaml) ' + ' Secondary backup schedule ' + ' '; var hddCheckbox = ''; if (hddResp.ok && hddResp.data && hddResp.data.has_hdd_data) { var hddPaths = ''; hddResp.data.hdd_paths.forEach(function(p) { hddPaths += ' ' + p.path + ' (' + (p.exists ?",
|
||||
"Data you keep is NOT backed up automatically any more.' + ' '; hddCheckbox = '\\x3Cscript>document.getElementById(\"remove-hdd-check\").addEventListener(\"change\",function(){document.getElementById(\"remove-hdd-keep-warning\").style.display=this.checked?\"none\":\"\";}); '; } var backupCheckbox = ''; if (backupResp.ok && backupResp.data && backupResp.data.has_backups) { var bkPaths = ''; backupResp.data.backup_paths.forEach(function(p) { if (p.exists) bkPaths += ' ' + p.path + ' (' + p.size_human + ') '; }); if (bkPaths) { sections += ' ' + ' Backup data: ' + bkPaths + ' Delete backup data ' + ' ' + 'The nightly restic snapshots cannot be deleted one by one — they expire automatically under the retention rules.' + ' '; } } modal.querySelector('.modal-card').innerHTML = ' Remove app: ' + name + ' ' + ' The app goes back to \"Not installed\"."
|
||||
]
|
||||
},
|
||||
"observables_after": {
|
||||
"pinned_images": {
|
||||
"docmost": "docmost/docmost:0.96.0",
|
||||
"docmost-postgres": "postgres:17-alpine",
|
||||
"docmost-redis": "redis:7-alpine"
|
||||
},
|
||||
"installed_images": {
|
||||
"docmost": "docmost/docmost:0.96.0",
|
||||
"docmost-postgres": "postgres:16-alpine",
|
||||
"docmost-redis": "redis:7-alpine"
|
||||
},
|
||||
"catalog_images": {
|
||||
"docmost": "docmost/docmost:0.96.0",
|
||||
"docmost-postgres": "postgres:17-alpine",
|
||||
"docmost-redis": "redis:7-alpine"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: docmost/docmost:0.96.0",
|
||||
"image: postgres:17-alpine",
|
||||
"image: redis:7-alpine"
|
||||
],
|
||||
"docker_inspect": []
|
||||
},
|
||||
"way_out": {
|
||||
"ok": true,
|
||||
"snapshot_id": "helyi",
|
||||
"snapshots": [
|
||||
{
|
||||
"time": "2026-09-21T19:44:15Z",
|
||||
"short_id": "helyi",
|
||||
"tier": 1,
|
||||
"drive_label": "Belső SSD (rendszer)"
|
||||
}
|
||||
],
|
||||
"http": "HTTP/2 302",
|
||||
"location": [
|
||||
"location: /backups/restore?flash=flash.restore.started"
|
||||
],
|
||||
"seconds": 30.4,
|
||||
"state_after": "running",
|
||||
"hold_after": null,
|
||||
"observables_after": {
|
||||
"pinned_images": {
|
||||
"docmost": "docmost/docmost:0.96.0",
|
||||
"docmost-postgres": "postgres:16-alpine",
|
||||
"docmost-redis": "redis:7-alpine"
|
||||
},
|
||||
"installed_images": {
|
||||
"docmost": "docmost/docmost:0.96.0",
|
||||
"docmost-postgres": "postgres:16-alpine",
|
||||
"docmost-redis": "redis:7-alpine"
|
||||
},
|
||||
"catalog_images": {
|
||||
"docmost": "docmost/docmost:0.96.0",
|
||||
"docmost-postgres": "postgres:17-alpine",
|
||||
"docmost-redis": "redis:7-alpine"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: docmost/docmost:0.96.0",
|
||||
"image: postgres:16-alpine",
|
||||
"image: redis:7-alpine"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"docmost docmost/docmost:0.96.0 running=true restarts=0",
|
||||
"docmost-redis redis:7-alpine running=true restarts=0",
|
||||
"docmost-postgres postgres:16-alpine running=true restarts=0"
|
||||
]
|
||||
}
|
||||
},
|
||||
"seed_read_after_restore": true,
|
||||
"engine_state_after_restore": "=== [1] PG_VERSION — the datadir's own record of its major\n16\n16\n=== [2] the engine's REFUSAL, verbatim (the whole point of this leg)\n\nPostgreSQL Database directory appears to contain a database; Skipping initialization\n\n2026-09-21 21:44:28.155 CEST [1] LOG: starting PostgreSQL 16.15 on x86_64-pc-linux-musl, compiled by gcc (Alpine 15.2.0) 15.2.0, 64-bit\n2026-09-21 21:44:28.155 CEST [1] LOG: listening on IPv4 address \"0.0.0.0\", port 5432\n2026-09-21 21:44:28.155 CEST [1] LOG: listening on IPv6 address \"::\", port 5432\n2026-09-21 21:44:28.161 CEST [1] LOG: listening on Unix socket \"/var/run/postgresql/.s.PGSQL.5432\"\n2026-09-21 21:44:28.169 CEST [29] LOG: database system was shut down at 2026-09-21 21:42:44 CEST\n2026-09-21 21:44:28.183 CEST [1] LOG: database system is ready to accept connections\n=== [3] container state\nimage=postgres:16-alpine running=true restarts=0 exit=0\n=== [4] the version binary in the image\npostgres (PostgreSQL) 16.15\n",
|
||||
"hold_sentences_after_restore": {
|
||||
"hu": [
|
||||
"Docmost — Felhom.eu document.documentElement.className=document.documentElement.className.replace('no-js','js'); function csrfHeaders(){var el=document.querySelector('meta[name=\"csrf-token\"]');return el?{'X-CSRF-Token':el.content}:{};} function felhomConfirm(el,question,onYes){ if(!el||el.dataset.fcOpen)return; el.dataset.fcOpen='1'; var wrap=document.createElement('span');wrap.className='inline-confirm'; var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question; var yes=document.createElement('button');yes.type='button';yes.className='btn btn-xs btn-danger';yes.textContent='Igen'; var no=document.createElement('button');no.type='button';no.className='btn btn-xs btn-outline';no.textContent='Mégse'; wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no); el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling); function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;} no.addEventListener('click',close); yes.addEventListener('click',function(){close();onYes();}); } document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('[data-confirm]'):null; if(!btn)return; e.preventDefault(); felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){ var form=btn.closest('form'); if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();} }); }); document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null; if(!btn)return; var group=btn.closest('.nav-group'); if(!group)return; var willOpen=!group.classList.contains('is-open'); document.querySelectorAll('.nav-group.is-open').forEach(function(g){ g.classList.remove('is-open'); var t=g.querySelector('.nav-group-toggle'); if(t)t.setAttribute('aria-expanded','false'); }); if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');} }); function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML=' Üzenet '+msg.replace(/&/g,'&').replace(/ /g,'>')+' OK ';o.appendChild(c);document.body.appendChild(o);} Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások Docmost Fut Frissítés elérhető — ma Megnyitás ↗ Napló Exportálás Beállítások Modern wiki és dokumentáció platform Notion-szerű szerkesztővel ~200M RAM productivity Csak x86 Áthelyezés másik tárhelyre Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át.",
|
||||
"Csapat tudásbázis és belső dokumentáció Notion-szerű blokk szerkesztő valós idejű együttműködéssel Terek és oldalak hierarchikus szervezése Képek, táblázatok és kód blokkok beágyazása Keresés a teljes dokumentációban Első lépések Nyisd meg a docs.DOMAIN címet a böngészőben Az első regisztrált felhasználó automatikusan admin lesz Hozd létre az első teret (Space) a dokumentumoknak Kezdj el írni a Notion-szerű szerkesztőben Előfeltételek x86 processzor szükséges Legalább 768 MB szabad RAM (Docmost + PostgreSQL + Redis) Dokumentáció Hivatalos dokumentáció ↗ (function(){ var burger=document.querySelector('.nav-burger'); var sidebar=document.getElementById('sidebar'); var backdrop=document.querySelector('.nav-backdrop'); if(!burger||!sidebar||!backdrop)return; function setOpen(open){ sidebar.classList.toggle('is-open',open); document.body.classList.toggle('nav-open',open); backdrop.hidden=!open; burger.setAttribute('aria-expanded',open?'true':'false'); } burger.addEventListener('click',function(){setOpen(!sidebar.classList.contains('is-open'));}); backdrop.addEventListener('click',function(){setOpen(false);}); document.addEventListener('keydown',function(e){ if(e.key==='Escape'&&sidebar.classList.contains('is-open'))setOpen(false); }); })(); document.addEventListener('click', function(e) { if (e.target.closest('a, button, .btn, input, select, textarea, .app-row-actions, .stack-detail-actions')) return; var card = e.target.closest('[data-href]'); if (card) window.location.href = card.dataset.href; }); async function checkBeforeDeploy(e, name) { try { var resp = await fetch('/api/stacks/' + name); var data = await resp.json(); if (data.ok && data.data && data.data.deployed) { e.preventDefault(); showAlert('Ez az alkalmazás már telepítve van.'); window.location.reload(); return false; } } catch(err) {} return true; } async function syncTemplates() { const btn = document.getElementById('sync-btn'); const toast = document.getElementById('sync-toast'); if (!btn) return; const origText = btn.innerHTML; btn.disabled = true; btn.innerHTML = '↻ Frissítés...'; btn.classList.add('loading'); try { const resp = await fetch('/api/sync', { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (toast) { toast.textContent = data.ok ?",
|
||||
"(data.message || 'Sablonok frissítve') : ('Hiba: ' + (data.error || 'Ismeretlen hiba')); toast.className = 'sync-toast ' + (data.ok ?",
|
||||
"'sync-toast-ok' : 'sync-toast-err'); toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) { setTimeout(function() { window.location.reload(); }, 1500); } } catch (err) { if (toast) { toast.textContent = 'Hálózati hiba: ' + err.message; toast.className = 'sync-toast sync-toast-err'; toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } } btn.innerHTML = origText; btn.disabled = false; btn.classList.remove('loading'); } async function stackAction(event, name, action) { const btn = event.currentTarget; const origText = btn.textContent; btn.disabled = true; btn.textContent = 'Folyamatban...'; btn.classList.add('loading'); try { const resp = await fetch('/api/stacks/' + name + '/' + action, { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (!data.ok) { showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba')); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); return; } if (action === 'update') { followUpdate(name, btn); return; } window.location.reload(); } catch (err) { showAlert('Hálózati hiba: ' + err.message); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); } } function followUpdate(name, btn) { var started = Date.now(); var timer = setInterval(async function() { if (Date.now() - started > 30 * 60 * 1000) { clearInterval(timer); window.location.reload(); return; } try { var r = await fetch('/api/stacks/' + name); var d = await r.json(); if (!d.ok || !d.data) return; if (d.data.update_phase_label) btn.textContent = d.data.update_phase_label; if (!d.data.updating) { clearInterval(timer); window.location.reload(); } } catch (e) {} }, 3000); } async function deleteOrphanStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'delete-modal'; modal.innerHTML = ' Betöltés...",
|
||||
"' + ' Ez a művelet nem visszavonható!",
|
||||
"A megtartott adatok a továbbiakban NEM lesznek automatikusan mentve.' + ' '; hddCheckbox = '\\x3Cscript>document.getElementById(\"remove-hdd-check\").addEventListener(\"change\",function(){document.getElementById(\"remove-hdd-keep-warning\").style.display=this.checked?\"none\":\"\";}); '; } var backupCheckbox = ''; if (backupResp.ok && backupResp.data && backupResp.data.has_backups) { var bkPaths = ''; backupResp.data.backup_paths.forEach(function(p) { if (p.exists) bkPaths += ' ' + p.path + ' (' + p.size_human + ') '; }); if (bkPaths) { sections += ' ' + ' Mentési adatok: ' + bkPaths + ' Mentési adatok törlése ' + ' ' + 'Az éjszakai restic pillanatképek nem törölhetők egyenként — a megőrzési szabályok szerint automatikusan elavulnak.' + ' '; } } modal.querySelector('.modal-card').innerHTML = ' Alkalmazás eltávolítása: ' + name + ' ' + ' Az alkalmazás visszaáll \"Nincs telepítve\" állapotba.",
|
||||
"' + ' Ez a művelet nem visszavonható!"
|
||||
],
|
||||
"en": [
|
||||
"Docmost — Felhom.eu document.documentElement.className=document.documentElement.className.replace('no-js','js'); function csrfHeaders(){var el=document.querySelector('meta[name=\"csrf-token\"]');return el?{'X-CSRF-Token':el.content}:{};} function felhomConfirm(el,question,onYes){ if(!el||el.dataset.fcOpen)return; el.dataset.fcOpen='1'; var wrap=document.createElement('span');wrap.className='inline-confirm'; var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question; var yes=document.createElement('button');yes.type='button';yes.className='btn btn-xs btn-danger';yes.textContent='Yes'; var no=document.createElement('button');no.type='button';no.className='btn btn-xs btn-outline';no.textContent='Cancel'; wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no); el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling); function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;} no.addEventListener('click',close); yes.addEventListener('click',function(){close();onYes();}); } document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('[data-confirm]'):null; if(!btn)return; e.preventDefault(); felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){ var form=btn.closest('form'); if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();} }); }); document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null; if(!btn)return; var group=btn.closest('.nav-group'); if(!group)return; var willOpen=!group.classList.contains('is-open'); document.querySelectorAll('.nav-group.is-open').forEach(function(g){ g.classList.remove('is-open'); var t=g.querySelector('.nav-group-toggle'); if(t)t.setAttribute('aria-expanded','false'); }); if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');} }); function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML=' Message '+msg.replace(/&/g,'&').replace(/ /g,'>')+' OK ';o.appendChild(c);document.body.appendChild(o);} Launcher Dashboard Apps Storage Drives Network storage Backup Overview Remote backup Apps Restore Sharing Network sharing System monitor Debug Settings System Notifications Security and access 0.261.0 Magyar English Sign out ↗ The hub connection is off — central monitoring is not running System monitor → ← Apps Docmost Running Update available — today Open ↗ Log Export Settings A modern wiki and documentation platform with a Notion-like editor ~200M RAM productivity x86 only Move to another storage You can move this app’s data to another connected storage.",
|
||||
"'sync-toast-ok' : 'sync-toast-err'); toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) { setTimeout(function() { window.location.reload(); }, 1500); } } catch (err) { if (toast) { toast.textContent = 'Network error: ' + err.message; toast.className = 'sync-toast sync-toast-err'; toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } } btn.innerHTML = origText; btn.disabled = false; btn.classList.remove('loading'); } async function stackAction(event, name, action) { const btn = event.currentTarget; const origText = btn.textContent; btn.disabled = true; btn.textContent = 'In progress...'; btn.classList.add('loading'); try { const resp = await fetch('/api/stacks/' + name + '/' + action, { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (!data.ok) { showAlert('Error: ' + (data.error || 'Unknown error')); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); return; } if (action === 'update') { followUpdate(name, btn); return; } window.location.reload(); } catch (err) { showAlert('Network error: ' + err.message); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); } } function followUpdate(name, btn) { var started = Date.now(); var timer = setInterval(async function() { if (Date.now() - started > 30 * 60 * 1000) { clearInterval(timer); window.location.reload(); return; } try { var r = await fetch('/api/stacks/' + name); var d = await r.json(); if (!d.ok || !d.data) return; if (d.data.update_phase_label) btn.textContent = d.data.update_phase_label; if (!d.data.updating) { clearInterval(timer); window.location.reload(); } } catch (e) {} }, 3000); } async function deleteOrphanStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'delete-modal'; modal.innerHTML = ' Loading..."
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -18,7 +18,6 @@
|
||||
"measured_at": "2026-09-21T18:37:10.025072+00:00",
|
||||
"evidence": "apps/gitea/",
|
||||
"notes": [
|
||||
"seed refused through the app's own route — see log.txt for what was tried",
|
||||
"seed route did not work tonight"
|
||||
"INCONCLUSIVE: the template sets no `INSTALL_LOCK`, so a fresh Gitea starts in its web-installer state and `gitea admin user create` refuses with `MustInstalled() [F] Unable to load config file for a installed Gitea instance`. The route that would work is POSTing the installer form first; that was not written tonight and is listed as owed rather than faked."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
mealie | Switching to dedicated user
|
||||
mealie |
|
||||
mealie | User uid: 1000
|
||||
mealie | User gid: 1000
|
||||
mealie |
|
||||
mealie | INFO 2026-09-21T21:24:18 - setup plugin alembic.autogenerate.schemas
|
||||
mealie | INFO 2026-09-21T21:24:18 - setup plugin alembic.autogenerate.tables
|
||||
mealie | INFO 2026-09-21T21:24:18 - setup plugin alembic.autogenerate.types
|
||||
mealie | INFO 2026-09-21T21:24:18 - setup plugin alembic.autogenerate.constraints
|
||||
mealie | INFO 2026-09-21T21:24:18 - setup plugin alembic.autogenerate.defaults
|
||||
mealie | INFO 2026-09-21T21:24:18 - setup plugin alembic.autogenerate.comments
|
||||
mealie | INFO 2026-09-21T21:24:24 - Started server process [1]
|
||||
mealie | INFO 2026-09-21T21:24:24 - Waiting for application startup.
|
||||
mealie | INFO 2026-09-21T21:24:24 - start: database initialization
|
||||
mealie | INFO 2026-09-21T21:24:24 - Database connection established.
|
||||
mealie | INFO 2026-09-21T21:24:24 - Context impl SQLiteImpl.
|
||||
mealie | INFO 2026-09-21T21:24:24 - Will assume non-transactional DDL.
|
||||
mealie | INFO 2026-09-21T21:24:25 - end: database initialization
|
||||
mealie | INFO 2026-09-21T21:24:25 - -----SYSTEM STARTUP-----
|
||||
mealie | INFO 2026-09-21T21:24:25 - ------APP SETTINGS------
|
||||
mealie | INFO 2026-09-21T21:24:25 - {
|
||||
mealie | "TESTING": false,
|
||||
mealie | "PRODUCTION": true,
|
||||
mealie | "LOG_CONFIG_OVERRIDE": null,
|
||||
mealie | "LOG_LEVEL": "info",
|
||||
mealie | "theme": {
|
||||
mealie | "light_primary": "#E58325",
|
||||
mealie | "light_accent": "#007A99",
|
||||
mealie | "light_secondary": "#973542",
|
||||
mealie | "light_success": "#43A047",
|
||||
mealie | "light_info": "#1976D2",
|
||||
mealie | "light_warning": "#FF6D00",
|
||||
mealie | "light_error": "#EF5350",
|
||||
mealie | "dark_primary": "#E58325",
|
||||
mealie | "dark_accent": "#007A99",
|
||||
mealie | "dark_secondary": "#973542",
|
||||
mealie | "dark_success": "#43A047",
|
||||
mealie | "dark_info": "#1976D2",
|
||||
mealie | "dark_warning": "#FF6D00",
|
||||
mealie | "dark_error": "#EF5350"
|
||||
mealie | },
|
||||
mealie | "BASE_URL": "https://mealie.enkisfelhom.hu",
|
||||
mealie | "STATIC_FILES": "/opt/mealie/lib/python3.12/site-packages/mealie/frontend",
|
||||
mealie | "IS_DEMO": false,
|
||||
mealie | "HOST_IP": "*",
|
||||
mealie | "API_HOST": "0.0.0.0",
|
||||
mealie | "API_PORT": 9000,
|
||||
mealie | "API_DOCS": true,
|
||||
mealie | "TOKEN_TIME": 48,
|
||||
mealie | "GIT_COMMIT_HASH": "a562409e43964d3734eabd64e4a2fd64bffaab4d",
|
||||
mealie | "ALLOW_SIGNUP": false,
|
||||
mealie | "ALLOW_PASSWORD_LOGIN": true,
|
||||
mealie | "ALLOWED_IFRAME_HOSTS": "",
|
||||
mealie | "DAILY_SCHEDULE_TIME": "23:45",
|
||||
mealie | "SECURITY_MAX_LOGIN_ATTEMPTS": 5,
|
||||
mealie | "SECURITY_USER_LOCKOUT_TIME": 24,
|
||||
mealie | "DB_ENGINE": "sqlite",
|
||||
mealie | "SQLITE_MIGRATE_JOURNAL_WAL": false,
|
||||
mealie | "DEFAULT_GROUP": "Home",
|
||||
mealie | "DEFAULT_HOUSEHOLD": "Family",
|
||||
mealie | "SMTP_HOST": "",
|
||||
mealie | "SMTP_PORT": "25",
|
||||
mealie | "SMTP_FROM_NAME": "",
|
||||
mealie | "SMTP_FROM_EMAIL": "",
|
||||
mealie | "SMTP_USER": null,
|
||||
mealie | "SMTP_PASSWORD": null,
|
||||
mealie | "SMTP_AUTH_STRATEGY": "NONE",
|
||||
mealie | "LDAP_AUTH_ENABLED": false,
|
||||
mealie | "LDAP_SERVER_URL": null,
|
||||
mealie | "LDAP_TLS_INSECURE": false,
|
||||
mealie | "LDAP_TLS_CACERTFILE": null,
|
||||
mealie | "LDAP_ENABLE_STARTTLS": false,
|
||||
mealie | "LDAP_BASE_DN": null,
|
||||
mealie | "LDAP_QUERY_BIND": null,
|
||||
mealie | "LDAP_QUERY_PASSWORD": null,
|
||||
mealie | "LDAP_USER_FILTER": null,
|
||||
mealie | "LDAP_ADMIN_FILTER": null,
|
||||
mealie | "LDAP_ID_ATTRIBUTE": "uid",
|
||||
mealie | "LDAP_MAIL_ATTRIBUTE": "mail",
|
||||
mealie | "LDAP_NAME_ATTRIBUTE": "name",
|
||||
mealie | "OIDC_AUTH_ENABLED": false,
|
||||
mealie | "OIDC_CLIENT_ID": null,
|
||||
mealie | "OIDC_CLIENT_SECRET": null,
|
||||
mealie | "OIDC_CONFIGURATION_URL": null,
|
||||
mealie | "OIDC_SIGNUP_ENABLED": true,
|
||||
mealie | "OIDC_USER_GROUP": null,
|
||||
mealie | "OIDC_ADMIN_GROUP": null,
|
||||
mealie | "OIDC_AUTO_REDIRECT": false,
|
||||
mealie | "OIDC_PROVIDER_NAME": "OAuth",
|
||||
mealie | "OIDC_REMEMBER_ME": false,
|
||||
mealie | "OIDC_USER_CLAIM": "email",
|
||||
mealie | "OIDC_NAME_CLAIM": "name",
|
||||
mealie | "OIDC_GROUPS_CLAIM": "groups",
|
||||
mealie | "OIDC_SCOPES_OVERRIDE": null,
|
||||
mealie | "OIDC_TLS_CACERTFILE": null,
|
||||
mealie | "OIDC_CLIENT_TIMEOUT": "default",
|
||||
mealie | "OPENAI_CUSTOM_PROMPT_DIR": null,
|
||||
mealie | "WORKER_PER_CORE": 1,
|
||||
mealie | "UVICORN_WORKERS": 1,
|
||||
mealie | "TLS_CERTIFICATE_PATH": null,
|
||||
mealie | "TLS_PRIVATE_KEY_PATH": null
|
||||
mealie | }
|
||||
mealie | INFO 2026-09-21T21:24:25 - ------APP FEATURES------
|
||||
mealie | INFO 2026-09-21T21:24:25 - --------==SMTP==--------
|
||||
mealie | INFO 2026-09-21T21:24:25 - Enabled: False
|
||||
mealie | INFO 2026-09-21T21:24:25 - --------==LDAP==--------
|
||||
mealie | INFO 2026-09-21T21:24:25 - Enabled: False
|
||||
mealie | Reason: LDAP_AUTH_ENABLED is false
|
||||
mealie | INFO 2026-09-21T21:24:25 - --------==OIDC==--------
|
||||
mealie | INFO 2026-09-21T21:24:25 - Enabled: False
|
||||
mealie | Reason: OIDC_AUTH_ENABLED is false
|
||||
mealie | INFO 2026-09-21T21:24:25 - ------------------------
|
||||
mealie | INFO 2026-09-21T21:24:25 - Daily tasks scheduled for 2026-09-21 21:45:00+00:00
|
||||
mealie | INFO 2026-09-21T21:24:25 - Application startup complete.
|
||||
mealie | INFO 2026-09-21T21:24:25 - Uvicorn running on http://0.0.0.0:9000 (Press CTRL+C to quit)
|
||||
mealie | INFO 2026-09-21T21:25:08 - [192.168.0.180:0] 200 OK "GET /api/app/about HTTP/1.1"
|
||||
mealie | INFO 2026-09-21T21:25:08 - [192.168.0.180:0] 200 OK "POST /api/auth/token HTTP/1.1"
|
||||
mealie | ERROR 2026-09-21T21:25:08 - No Entry Found on recipe controller action
|
||||
mealie | ERROR 2026-09-21T21:25:08 - No Entry Found on recipe controller action
|
||||
mealie | INFO 2026-09-21T21:25:08 - [192.168.0.180:0] 404 Not Found "GET /api/recipes/nopefec546301e HTTP/1.1"
|
||||
mealie | INFO 2026-09-21T21:25:08 - [192.168.0.180:0] 200 OK "GET /api/recipes/drill-087e4023c5 HTTP/1.1"
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"before": {
|
||||
"hu": [
|
||||
{
|
||||
"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.",
|
||||
"text": "Naprakész"
|
||||
}
|
||||
],
|
||||
"en": [
|
||||
{
|
||||
"title": "This app is running the newest version available.",
|
||||
"text": "Up to date"
|
||||
}
|
||||
]
|
||||
},
|
||||
"after": {
|
||||
"hu": [
|
||||
{
|
||||
"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.",
|
||||
"text": "Naprakész"
|
||||
}
|
||||
],
|
||||
"en": [
|
||||
{
|
||||
"title": "This app is running the newest version available.",
|
||||
"text": "Up to date"
|
||||
}
|
||||
]
|
||||
},
|
||||
"drill_commit": "91e4bb97212e"
|
||||
}
|
||||
@@ -1,3 +1,11 @@
|
||||
21:02:15 ==== mealie: ghcr.io/mealie-recipes/mealie:v3.20.1 -> ghcr.io/mealie-recipes/mealie:v3.27.0 (sub=recipes, class=db-postgres)
|
||||
21:02:15 [1] deploy -> 500 {'ok': False, 'error': 'a(z) "recipes" aldomain már használatban van egy másik alkalmazásban'}
|
||||
21:02:15 [9] verdict inconclusive -> /mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/update-night-2026-09-21/apps/mealie/verdict.json
|
||||
21:31:06 ==== mealie: ghcr.io/mealie-recipes/mealie:v3.20.1 -> ghcr.io/mealie-recipes/mealie:v3.27.0 (sub=mealie, class=db-postgres)
|
||||
21:31:07 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
|
||||
21:32:17 [1] deployed, controller state=running, pinned={'mealie': 'ghcr.io/mealie-recipes/mealie:v3.27.0'}
|
||||
21:32:20 [2] seeding through the app's own front door
|
||||
21:32:21 mealie: create recipe http=201
|
||||
21:32:21 [3] control C1 — reading the seed back BEFORE the update
|
||||
21:32:21 mealie: readback of the seeded recipe http=200 ok=True
|
||||
21:32:21 [4] „Mentés most" -> 200 {'ok': True, 'message': 'Mentés elindítva'}
|
||||
21:33:06 [4] backup idle; last=None
|
||||
21:33:06 [5] FROM ref not found in compose: ghcr.io/mealie-recipes/mealie:v3.20.1
|
||||
21:33:06 [9] verdict inconclusive -> /mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/update-night-2026-09-21/apps/mealie/verdict.json
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"pinned_images": {
|
||||
"mealie": "ghcr.io/mealie-recipes/mealie:v3.27.0"
|
||||
},
|
||||
"installed_images": {
|
||||
"mealie": "ghcr.io/mealie-recipes/mealie:v3.27.0"
|
||||
},
|
||||
"catalog_images": null,
|
||||
"live_compose_image_lines": [
|
||||
"image: ghcr.io/mealie-recipes/mealie:v3.27.0"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"mealie ghcr.io/mealie-recipes/mealie:v3.27.0 running=true restarts=0"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"before": {
|
||||
"pinned_images": {
|
||||
"mealie": "ghcr.io/mealie-recipes/mealie:v3.20.1"
|
||||
},
|
||||
"installed_images": {
|
||||
"mealie": "ghcr.io/mealie-recipes/mealie:v3.20.1"
|
||||
},
|
||||
"catalog_images": {
|
||||
"mealie": "ghcr.io/mealie-recipes/mealie:v3.20.1"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: ghcr.io/mealie-recipes/mealie:v3.20.1"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"mealie ghcr.io/mealie-recipes/mealie:v3.20.1 running=true restarts=0"
|
||||
]
|
||||
},
|
||||
"after": {
|
||||
"pinned_images": {
|
||||
"mealie": "ghcr.io/mealie-recipes/mealie:v3.20.1"
|
||||
},
|
||||
"installed_images": {
|
||||
"mealie": "ghcr.io/mealie-recipes/mealie:v3.20.1"
|
||||
},
|
||||
"catalog_images": {
|
||||
"mealie": "ghcr.io/mealie-recipes/mealie:v3.20.1"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: ghcr.io/mealie-recipes/mealie:v3.20.1"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"mealie ghcr.io/mealie-recipes/mealie:v3.20.1 running=true restarts=0"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"accepted": true,
|
||||
"http": "202",
|
||||
"phases": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"phase": "safety-dump",
|
||||
"label": "Adatbázis pillanatkép…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 1.1,
|
||||
"phase": "verifying",
|
||||
"label": "Működés ellenőrzése…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 2.1,
|
||||
"phase": "done",
|
||||
"label": "Frissítve",
|
||||
"updating": false,
|
||||
"error": null,
|
||||
"hold": null
|
||||
}
|
||||
],
|
||||
"duration_s": 3.1,
|
||||
"final_phase": "done",
|
||||
"update_error": null,
|
||||
"hold_reason": null,
|
||||
"state": "running"
|
||||
}
|
||||
@@ -3,19 +3,21 @@
|
||||
"app": "mealie",
|
||||
"venue": "guest 9202 demo-hp-scratch, controller 0.261.0",
|
||||
"class": "db-postgres",
|
||||
"from": {},
|
||||
"from": {
|
||||
"mealie": "ghcr.io/mealie-recipes/mealie:v3.27.0"
|
||||
},
|
||||
"to": {},
|
||||
"verdict": "inconclusive",
|
||||
"seed_read_before": false,
|
||||
"seed_read_before": true,
|
||||
"seed_read_after": false,
|
||||
"healthy_after": false,
|
||||
"migration_observed": null,
|
||||
"abort": "not-attempted",
|
||||
"abort_detail": null,
|
||||
"duration_s": 0.3,
|
||||
"measured_at": "2026-09-21T19:02:15.239347+00:00",
|
||||
"duration_s": 120.0,
|
||||
"measured_at": "2026-09-21T19:31:06.937002+00:00",
|
||||
"evidence": "apps/mealie/",
|
||||
"notes": [
|
||||
"deploy never reached running — nothing else could be measured"
|
||||
"the drill bump could not be committed — the FROM ref did not match the template"
|
||||
]
|
||||
}
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
nextcloud | => Configuring PHP session handler...
|
||||
nextcloud | ==> Using Redis as PHP session handler...
|
||||
nextcloud | => Searching for hook scripts (*.sh) to run, located in the folder "/docker-entrypoint-hooks.d/before-starting"
|
||||
nextcloud | ==> Skipped: the "before-starting" folder is empty (or does not exist)
|
||||
nextcloud | AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.22.0.4. Set the 'ServerName' directive globally to suppress this message
|
||||
nextcloud | AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.22.0.4. Set the 'ServerName' directive globally to suppress this message
|
||||
nextcloud | [Mon Sep 21 21:38:55.360982 2026] [mpm_prefork:notice] [pid 1:tid 1] AH00163: Apache/2.4.68 (Debian) PHP/8.5.8 configured -- resuming normal operations
|
||||
nextcloud-db | 2026-09-21 21:40:20+02:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:12.3.3+maria~ubu2404 started.
|
||||
nextcloud | [Mon Sep 21 21:38:55.361025 2026] [core:notice] [pid 1:tid 1] AH00094: Command line: 'apache2 -D FOREGROUND'
|
||||
nextcloud | 127.0.0.1 - - [21/Sep/2026:21:39:00 +0200] "GET /status.php HTTP/1.1" 200 1063 "-" "curl/8.14.1"
|
||||
nextcloud | 172.18.0.7 - - [21/Sep/2026:21:39:03 +0200] "GET /status.php HTTP/1.1" 200 1063 "-" "Go-http-client/1.1"
|
||||
nextcloud | 127.0.0.1 - - [21/Sep/2026:21:39:30 +0200] "GET /status.php HTTP/1.1" 200 1063 "-" "curl/8.14.1"
|
||||
nextcloud | 127.0.0.1 - - [21/Sep/2026:21:40:00 +0200] "GET /status.php HTTP/1.1" 200 1063 "-" "curl/8.14.1"
|
||||
nextcloud | 127.0.0.1 - - [21/Sep/2026:21:40:30 +0200] "GET /status.php HTTP/1.1" 500 376 "-" "curl/8.14.1"
|
||||
nextcloud | 172.18.0.7 - - [21/Sep/2026:21:40:36 +0200] "GET /status.php HTTP/1.1" 200 1063 "-" "Go-http-client/1.1"
|
||||
nextcloud | 192.168.0.180 - - [21/Sep/2026:21:40:40 +0200] "GET /status.php HTTP/1.1" 200 1063 "-" "curl/8.14.1"
|
||||
nextcloud-redis | 1:C 21 Sep 2026 21:38:49.342 # WARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. Being disabled, it can also cause failures without low memory condition, see https://github.com/jemalloc/jemalloc/issues/1328. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
|
||||
nextcloud-redis | 1:C 21 Sep 2026 21:38:49.342 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
|
||||
nextcloud-redis | 1:C 21 Sep 2026 21:38:49.342 * Redis version=7.4.11, bits=64, commit=00000000, modified=0, pid=1, just started
|
||||
nextcloud-redis | 1:C 21 Sep 2026 21:38:49.342 * Configuration loaded
|
||||
nextcloud-redis | 1:M 21 Sep 2026 21:38:49.342 * Increased maximum number of open files to 10032 (it was originally set to 1024).
|
||||
nextcloud-redis | 1:M 21 Sep 2026 21:38:49.342 * monotonic clock: POSIX clock_gettime
|
||||
nextcloud-redis | 1:M 21 Sep 2026 21:38:49.344 * Running mode=standalone, port=6379.
|
||||
nextcloud-redis | 1:M 21 Sep 2026 21:38:49.344 * Server initialized
|
||||
nextcloud-redis | 1:M 21 Sep 2026 21:38:49.345 * Reading RDB base file on AOF loading...
|
||||
nextcloud-redis | 1:M 21 Sep 2026 21:38:49.345 * Loading RDB produced by version 7.4.11
|
||||
nextcloud-redis | 1:M 21 Sep 2026 21:38:49.345 * RDB age 96 seconds
|
||||
nextcloud-redis | 1:M 21 Sep 2026 21:38:49.345 * RDB memory usage when created 0.90 Mb
|
||||
nextcloud-redis | 1:M 21 Sep 2026 21:38:49.345 * RDB is base AOF
|
||||
nextcloud-redis | 1:M 21 Sep 2026 21:38:49.345 * Done loading RDB, keys loaded: 0, keys expired: 0.
|
||||
nextcloud-redis | 1:M 21 Sep 2026 21:38:49.345 * DB loaded from base file appendonly.aof.1.base.rdb: 0.000 seconds
|
||||
nextcloud-redis | 1:M 21 Sep 2026 21:38:49.345 * DB loaded from incr file appendonly.aof.1.incr.aof: 0.000 seconds
|
||||
nextcloud-redis | 1:M 21 Sep 2026 21:38:49.345 * DB loaded from append only file: 0.000 seconds
|
||||
nextcloud-redis | 1:M 21 Sep 2026 21:38:49.345 * Opening AOF incr file appendonly.aof.1.incr.aof on server start
|
||||
nextcloud-db | 2026-09-21 21:40:20+02:00 [Warn] [Entrypoint]: /sys/fs/cgroup///memory.pressure not writable, functionality unavailable to MariaDB
|
||||
nextcloud-db | 2026-09-21 21:40:20+02:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
|
||||
nextcloud-db | 2026-09-21 21:40:20+02:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:12.3.3+maria~ubu2404 started.
|
||||
nextcloud-db | 2026-09-21 21:40:21+02:00 [Note] [Entrypoint]: Starting temporary server
|
||||
nextcloud-redis | 1:M 21 Sep 2026 21:38:49.345 * Ready to accept connections tcp
|
||||
nextcloud-db | 2026-09-21 21:40:21+02:00 [Note] [Entrypoint]: Waiting for server startup
|
||||
nextcloud-db | 2026-09-21 21:40:21 0 [Note] Starting MariaDB 12.3.3-MariaDB-ubu2404 source revision 83e909fc2a0dbc394b4b683fb3fa2d7dcf26cc5e server_uid 14CtiqEo7W/9wCYATc914eYJJhE= as process 56
|
||||
nextcloud-db | 2026-09-21 21:40:21 0 [Note] InnoDB: Compressed tables use zlib 1.3
|
||||
nextcloud-db | 2026-09-21 21:40:21 0 [Note] InnoDB: Number of transaction pools: 1
|
||||
nextcloud-db | 2026-09-21 21:40:21 0 [Note] InnoDB: Using crc32 + pclmulqdq instructions
|
||||
nextcloud-db | 2026-09-21 21:40:21 0 [Warning] mariadbd: io_uring_queue_init() failed with EPERM: sysctl kernel.io_uring_disabled has the value 2, or 1 and the user of the process is not a member of sysctl kernel.io_uring_group. (see man 2 io_uring_setup).
|
||||
nextcloud-db | create_uring failed: falling back to libaio
|
||||
nextcloud-db | 2026-09-21 21:40:21 0 [Note] InnoDB: Using Linux native AIO
|
||||
nextcloud-db | 2026-09-21 21:40:21 0 [Note] InnoDB: innodb_buffer_pool_size_max=8388608m, innodb_buffer_pool_size=128m
|
||||
nextcloud-db | 2026-09-21 21:40:21 0 [Note] InnoDB: Completed initialization of buffer pool
|
||||
nextcloud-db | 2026-09-21 21:40:21 0 [Note] InnoDB: File system buffers for log disabled (block size=512 bytes)
|
||||
nextcloud-db | 2026-09-21 21:40:21 0 [Note] InnoDB: End of log at LSN=2186147
|
||||
nextcloud-db | 2026-09-21 21:40:21 0 [Note] InnoDB: Opened 3 undo tablespaces
|
||||
nextcloud-db | 2026-09-21 21:40:21 0 [Note] InnoDB: 128 rollback segments in 3 undo tablespaces are active.
|
||||
nextcloud-db | 2026-09-21 21:40:21 0 [Note] InnoDB: Setting file './ibtmp1' size to 12.000MiB. Physically writing the file full; Please wait ...
|
||||
nextcloud-db | 2026-09-21 21:40:21 0 [Note] InnoDB: File './ibtmp1' size is now 12.000MiB.
|
||||
nextcloud-db | 2026-09-21 21:40:21 0 [Note] InnoDB: log sequence number 2186147; transaction id 2981
|
||||
nextcloud-db | 2026-09-21 21:40:21 0 [Note] Plugin 'FEEDBACK' is disabled.
|
||||
nextcloud-db | 2026-09-21 21:40:21 0 [Note] Plugin 'wsrep-provider' is disabled.
|
||||
nextcloud-db | 2026-09-21 21:40:22 0 [Note] Replication not automatically started: --skip-slave-start was specified
|
||||
nextcloud-db | 2026-09-21 21:40:22 0 [Note] mariadbd: ready for connections.
|
||||
nextcloud-db | Version: '12.3.3-MariaDB-ubu2404' socket: '/run/mysqld/mysqld.sock' port: 0 mariadb.org binary distribution
|
||||
nextcloud-db | 2026-09-21 21:40:23+02:00 [Note] [Entrypoint]: Temporary server started.
|
||||
nextcloud-db | 2026-09-21 21:40:23+02:00 [Note] [Entrypoint]: Backing up system database to system_mysql_backup_11.6.2-MariaDB.sql.zst
|
||||
nextcloud-db | 2026-09-21 21:40:23+02:00 [Note] [Entrypoint]: Backing up complete
|
||||
nextcloud-db | 2026-09-21 21:40:23+02:00 [Note] [Entrypoint]: Starting mariadb-upgrade
|
||||
nextcloud-db | The --upgrade-system-tables option was used, user tables won't be touched.
|
||||
nextcloud-db | Major version upgrade detected from 11.6.2-MariaDB to 12.3.3-MariaDB. Check required!
|
||||
nextcloud-db | Phase 1/8: Checking and upgrading mysql database
|
||||
nextcloud-db | Processing databases
|
||||
nextcloud-db | mysql
|
||||
nextcloud-db | mysql.column_stats OK
|
||||
nextcloud-db | mysql.columns_priv OK
|
||||
nextcloud-db | mysql.db OK
|
||||
nextcloud-db | mysql.event OK
|
||||
nextcloud-db | mysql.func OK
|
||||
nextcloud-db | mysql.global_priv OK
|
||||
nextcloud-db | mysql.gtid_slave_pos OK
|
||||
nextcloud-db | mysql.help_category OK
|
||||
nextcloud-db | mysql.help_keyword OK
|
||||
nextcloud-db | mysql.help_relation OK
|
||||
nextcloud-db | mysql.help_topic OK
|
||||
nextcloud-db | mysql.index_stats OK
|
||||
nextcloud-db | mysql.innodb_index_stats OK
|
||||
nextcloud-db | mysql.innodb_table_stats OK
|
||||
nextcloud-db | mysql.plugin OK
|
||||
nextcloud-db | mysql.proc OK
|
||||
nextcloud-db | mysql.procs_priv OK
|
||||
nextcloud-db | mysql.proxies_priv OK
|
||||
nextcloud-db | mysql.roles_mapping OK
|
||||
nextcloud-db | mysql.servers OK
|
||||
nextcloud-db | mysql.table_stats OK
|
||||
nextcloud-db | mysql.tables_priv OK
|
||||
nextcloud-db | mysql.time_zone OK
|
||||
nextcloud-db | mysql.time_zone_leap_second OK
|
||||
nextcloud-db | mysql.time_zone_name OK
|
||||
nextcloud-db | mysql.time_zone_transition OK
|
||||
nextcloud-db | mysql.time_zone_transition_type OK
|
||||
nextcloud-db | mysql.transaction_registry OK
|
||||
nextcloud-db | Phase 2/8: Installing used storage engines... Skipped
|
||||
nextcloud-db | Phase 3/8: Running 'mysql_fix_privilege_tables'
|
||||
nextcloud-db | Phase 4/8: Fixing views... Skipped
|
||||
nextcloud-db | Phase 5/8: Fixing table and database names ... Skipped
|
||||
nextcloud-db | Phase 6/8: Checking and upgrading tables... Skipped
|
||||
nextcloud-db | Phase 7/8: uninstalling plugins
|
||||
nextcloud-db | Phase 8/8: Running 'FLUSH PRIVILEGES'
|
||||
nextcloud-db | OK
|
||||
nextcloud-db | 2026-09-21 21:40:29+02:00 [Note] [Entrypoint]: Finished mariadb-upgrade
|
||||
nextcloud-db | 2026-09-21 21:40:29+02:00 [Note] [Entrypoint]: Stopping temporary server
|
||||
nextcloud-db | 2026-09-21 21:40:29 0 [Note] mariadbd (initiated by: unknown): Normal shutdown
|
||||
nextcloud-db | 2026-09-21 21:40:29 0 [Note] InnoDB: FTS optimize thread exiting.
|
||||
nextcloud-db | 2026-09-21 21:40:29 0 [Note] InnoDB: Starting shutdown...
|
||||
nextcloud-db | 2026-09-21 21:40:29 0 [Note] InnoDB: Removed temporary tablespace data file: "./ibtmp1"
|
||||
nextcloud-db | 2026-09-21 21:40:29 0 [Note] Shutdown completed; log sequence number 2186147; transaction id 2985
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] mariadbd: Shutdown complete
|
||||
nextcloud-db | 2026-09-21 21:40:30+02:00 [Note] [Entrypoint]: Temporary server stopped
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] Starting MariaDB 12.3.3-MariaDB-ubu2404 source revision 83e909fc2a0dbc394b4b683fb3fa2d7dcf26cc5e server_uid 14CtiqEo7W/9wCYATc914eYJJhE= as process 1
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] InnoDB: Compressed tables use zlib 1.3
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] InnoDB: Number of transaction pools: 1
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] InnoDB: Using crc32 + pclmulqdq instructions
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Warning] mariadbd: io_uring_queue_init() failed with EPERM: sysctl kernel.io_uring_disabled has the value 2, or 1 and the user of the process is not a member of sysctl kernel.io_uring_group. (see man 2 io_uring_setup).
|
||||
nextcloud-db | create_uring failed: falling back to libaio
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] InnoDB: Using Linux native AIO
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] InnoDB: innodb_buffer_pool_size_max=8388608m, innodb_buffer_pool_size=128m
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] InnoDB: Completed initialization of buffer pool
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] InnoDB: File system buffers for log disabled (block size=512 bytes)
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] InnoDB: End of log at LSN=2186147
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] InnoDB: Opened 3 undo tablespaces
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] InnoDB: 128 rollback segments in 3 undo tablespaces are active.
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] InnoDB: Setting file './ibtmp1' size to 12.000MiB. Physically writing the file full; Please wait ...
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] InnoDB: File './ibtmp1' size is now 12.000MiB.
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] InnoDB: log sequence number 2186147; transaction id 2981
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] Plugin 'FEEDBACK' is disabled.
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] Plugin 'wsrep-provider' is disabled.
|
||||
nextcloud-db | 2026-09-21 21:40:30 0 [Note] InnoDB: Buffer pool(s) load completed at 260921 21:40:30
|
||||
nextcloud-db | 2026-09-21 21:40:33 0 [Note] Server socket created on IP: '0.0.0.0', port: '3306'.
|
||||
nextcloud-db | 2026-09-21 21:40:33 0 [Note] Server socket created on IP: '::', port: '3306'.
|
||||
nextcloud-db | 2026-09-21 21:40:33 0 [Note] mariadbd: Event Scheduler: Loaded 0 events
|
||||
nextcloud-db | 2026-09-21 21:40:33 0 [Note] mariadbd: ready for connections.
|
||||
nextcloud-db | Version: '12.3.3-MariaDB-ubu2404' socket: '/run/mysqld/mysqld.sock' port: 3306 mariadb.org binary distribution
|
||||
nextcloud-db | 2026-09-21 21:40:39 6 [Warning] Access denied for user 'root'@'localhost' (using password: NO)
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"hu": [
|
||||
{
|
||||
"title": "Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.",
|
||||
"text": "Frissítés elérhető — ma"
|
||||
}
|
||||
],
|
||||
"en": [
|
||||
{
|
||||
"title": "A newer version of this app is available. Select the Update button to start it.",
|
||||
"text": "Update available — today"
|
||||
}
|
||||
]
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
=== [1] mariadb_upgrade_info — the datadir's OWN record of which version converted it
|
||||
12.3.3-MariaDB
|
||||
|
||||
=== [2] THE ENGINE'S OWN CHECK, authenticated (R-464: never the log line)
|
||||
This installation of MariaDB is already upgraded to 12.3.3-MariaDB.
|
||||
There is no need to run mariadb-upgrade again.
|
||||
exit=1
|
||||
=== [3] the entrypoint's own lines about the upgrade
|
||||
2026-09-21 21:40:23+02:00 [Note] [Entrypoint]: Starting mariadb-upgrade
|
||||
The --upgrade-system-tables option was used, user tables won't be touched.
|
||||
Major version upgrade detected from 11.6.2-MariaDB to 12.3.3-MariaDB. Check required!
|
||||
Phase 2/8: Installing used storage engines... Skipped
|
||||
Phase 4/8: Fixing views... Skipped
|
||||
Phase 5/8: Fixing table and database names ... Skipped
|
||||
Phase 6/8: Checking and upgrading tables... Skipped
|
||||
2026-09-21 21:40:29+02:00 [Note] [Entrypoint]: Finished mariadb-upgrade
|
||||
=== [4] the engine's OWN pre-upgrade backup file
|
||||
-rw-r--r-- 1 mysql mysql 631905 Sep 21 21:40 /var/lib/mysql/system_mysql_backup_11.6.2-MariaDB.sql.zst
|
||||
(no pre-upgrade backup file present)
|
||||
=== [5] the version actually running
|
||||
mariadbd Ver 12.3.3-MariaDB-ubu2404 for debian-linux-gnu on x86_64 (mariadb.org binary distribution)
|
||||
=== [6] container state
|
||||
image=mariadb:12.3 running=true restarts=0
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
=== [1] mariadb_upgrade_info (the datadir's own record of which version converted it)
|
||||
12.3.3-MariaDB
|
||||
=== [2] the engine's OWN check — NOT the log line (R-464)
|
||||
Reading datadir from the MariaDB server failed. Got the following error when executing the 'mysql' command line client
|
||||
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
|
||||
FATAL ERROR: Upgrade failed
|
||||
exit=1
|
||||
=== [3] the entrypoint's line, from the container log
|
||||
2026-09-21 21:40:23+02:00 [Note] [Entrypoint]: Starting mariadb-upgrade
|
||||
The --upgrade-system-tables option was used, user tables won't be touched.
|
||||
Major version upgrade detected from 11.6.2-MariaDB to 12.3.3-MariaDB. Check required!
|
||||
Phase 2/8: Installing used storage engines... Skipped
|
||||
Phase 4/8: Fixing views... Skipped
|
||||
Phase 5/8: Fixing table and database names ... Skipped
|
||||
Phase 6/8: Checking and upgrading tables... Skipped
|
||||
2026-09-21 21:40:29+02:00 [Note] [Entrypoint]: Finished mariadb-upgrade
|
||||
=== [4] the engine's own pre-upgrade backup file
|
||||
-rw-r--r-- 1 mysql mysql 631905 Sep 21 21:40 /var/lib/mysql/system_mysql_backup_11.6.2-MariaDB.sql.zst
|
||||
(no pre-upgrade backup file present)
|
||||
=== [5] the version actually running
|
||||
mariadbd Ver 12.3.3-MariaDB-ubu2404 for debian-linux-gnu on x86_64 (mariadb.org binary distribution)
|
||||
=== [6] container state
|
||||
image=mariadb:12.3 running=true restarts=0 exit=0
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
=== [1] mariadb_upgrade_info (the datadir's own record of which version converted it)
|
||||
11.6.2-MariaDB=== [2] the engine's OWN check — NOT the log line (R-464)
|
||||
Reading datadir from the MariaDB server failed. Got the following error when executing the 'mysql' command line client
|
||||
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
|
||||
FATAL ERROR: Upgrade failed
|
||||
exit=1
|
||||
=== [3] the entrypoint's line, from the container log
|
||||
=== [4] the engine's own pre-upgrade backup file
|
||||
(no pre-upgrade backup file present)
|
||||
=== [5] the version actually running
|
||||
mariadbd Ver 11.6.2-MariaDB-ubu2404 for debian-linux-gnu on x86_64 (mariadb.org binary distribution)
|
||||
=== [6] container state
|
||||
image=mariadb:11.6 running=true restarts=0 exit=0
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"hu": [
|
||||
"Nextcloud — Felhom.eu document.documentElement.className=document.documentElement.className.replace('no-js','js'); function csrfHeaders(){var el=document.querySelector('meta[name=\"csrf-token\"]');return el?{'X-CSRF-Token':el.content}:{};} function felhomConfirm(el,question,onYes){ if(!el||el.dataset.fcOpen)return; el.dataset.fcOpen='1'; var wrap=document.createElement('span');wrap.className='inline-confirm'; var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question; var yes=document.createElement('button');yes.type='button';yes.className='btn btn-xs btn-danger';yes.textContent='Igen'; var no=document.createElement('button');no.type='button';no.className='btn btn-xs btn-outline';no.textContent='Mégse'; wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no); el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling); function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;} no.addEventListener('click',close); yes.addEventListener('click',function(){close();onYes();}); } document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('[data-confirm]'):null; if(!btn)return; e.preventDefault(); felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){ var form=btn.closest('form'); if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();} }); }); document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null; if(!btn)return; var group=btn.closest('.nav-group'); if(!group)return; var willOpen=!group.classList.contains('is-open'); document.querySelectorAll('.nav-group.is-open').forEach(function(g){ g.classList.remove('is-open'); var t=g.querySelector('.nav-group-toggle'); if(t)t.setAttribute('aria-expanded','false'); }); if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');} }); function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML=' Üzenet '+msg.replace(/&/g,'&').replace(/ /g,'>')+' OK ';o.appendChild(c);document.body.appendChild(o);} Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások Nextcloud Fut Naprakész Megnyitás ↗ Napló Exportálás Beállítások Hiányzó tárhely: nextcloud Ennek az alkalmazásnak az adattárolója jelenleg nem elérhető, ezért le van állítva.",
|
||||
"Válassz céltárhelyet… SCRATCH HDD (/mnt/felhom-drives/scratch_hdd) Áthelyezés function appMigFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; } function appMigRender(job){ var names={stop:'Leállítás',copy:'Adatok másolása',verify:'Ellenőrzés',flip:'Újratelepítés',redeploy:'Újratelepítés',cleanup:'Régi adatok törlése'}; var s=names[job.phase]||job.phase; if(job.phase==='copy'&&job.bytes_total>0){ s+=' ('+Math.floor(100*job.bytes_done/job.bytes_total)+'% — '+appMigFmtGB(job.bytes_done)+'/'+appMigFmtGB(job.bytes_total)+')'; } return s+'…'; } function appMigWatch(){ var panel=document.getElementById('app-migrate-progress'); if(panel) panel.style.display='block'; function tick(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ var job=d.data&&d.data.job; if(!job){ if(panel) panel.textContent='Nincs folyamatban áthelyezés.'; return; } if(panel) panel.innerHTML=appMigRender(job); if(job.phase==='done'){ if(panel) panel.innerHTML+=' Kész '; setTimeout(function(){location.reload();},1500); return; } if(job.phase==='aborted'){ if(panel) panel.innerHTML+=' Megszakadt: '+(job.error||'')+' A régi adatok érintetlenek.'; return; } setTimeout(tick,1500); }).catch(function(){ setTimeout(tick,2000); }); } tick(); } function appMigrate(btn,app,label){ var sel=document.getElementById('app-migrate-target'); var target=sel?sel.value:''; if(!target){ showAlert('Válassz céltárhelyet.'); return; } felhomConfirm(btn,'Áthelyezed a(z) '+label+' adatait ide: '+target+'?",
|
||||
"A régi adatok csak sikeres áthelyezés után törlődnek.',function(){ fetch('/api/storage/migrate-app',{method:'POST',headers:Object.assign({'Content-Type':'application/json'},csrfHeaders()),body:JSON.stringify({app:app,target:target})}) .then(function(r){return r.json();}).then(function(d){ if(d.ok){ appMigWatch(); } else { showAlert('Hiba: '+(d.error||'ismeretlen')); } }) .catch(function(e){ showAlert('Hiba: '+e); }); }); } (function(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ if(d.data&&d.data.job){ appMigWatch(); } }).catch(function(){}); })(); Mire használható?",
|
||||
"Fájlok szinkronizálása és megosztása eszközök között Naptár és névjegyek szinkronizálás (CalDAV/CardDAV) Dokumentumok közös szerkesztése (OnlyOffice integrációval) Fotó és videó automatikus feltöltés telefonról Alkalmazásbolt - kibővíthető funkciók (Notes, Tasks, Forms, stb.) Első lépések Nyisd meg a cloud.DOMAIN címet a böngészőben Jelentkezz be a telepítéskor megadott admin fiókkal Telepítsd a Nextcloud asztali alkalmazást a számítógépedre Telepítsd a Nextcloud mobil alkalmazást a telefonodra Hívd meg a családtagokat felhasználói fiókok létrehozásával Előfeltételek Külső HDD szükséges a fájlok tárolásához Legalább 1 GB szabad RAM (Nextcloud + MariaDB + Redis) x86 processzor ajánlott a legjobb teljesítményhez Alapértelmezett belépés Az admin fiók adatait a telepítéskor adod meg Az első bejelentkezés után azonnal változtasd meg!",
|
||||
"Dokumentáció Hivatalos dokumentáció ↗ (function(){ var burger=document.querySelector('.nav-burger'); var sidebar=document.getElementById('sidebar'); var backdrop=document.querySelector('.nav-backdrop'); if(!burger||!sidebar||!backdrop)return; function setOpen(open){ sidebar.classList.toggle('is-open',open); document.body.classList.toggle('nav-open',open); backdrop.hidden=!open; burger.setAttribute('aria-expanded',open?'true':'false'); } burger.addEventListener('click',function(){setOpen(!sidebar.classList.contains('is-open'));}); backdrop.addEventListener('click',function(){setOpen(false);}); document.addEventListener('keydown',function(e){ if(e.key==='Escape'&&sidebar.classList.contains('is-open'))setOpen(false); }); })(); document.addEventListener('click', function(e) { if (e.target.closest('a, button, .btn, input, select, textarea, .app-row-actions, .stack-detail-actions')) return; var card = e.target.closest('[data-href]'); if (card) window.location.href = card.dataset.href; }); async function checkBeforeDeploy(e, name) { try { var resp = await fetch('/api/stacks/' + name); var data = await resp.json(); if (data.ok && data.data && data.data.deployed) { e.preventDefault(); showAlert('Ez az alkalmazás már telepítve van.'); window.location.reload(); return false; } } catch(err) {} return true; } async function syncTemplates() { const btn = document.getElementById('sync-btn'); const toast = document.getElementById('sync-toast'); if (!btn) return; const origText = btn.innerHTML; btn.disabled = true; btn.innerHTML = '↻ Frissítés...'; btn.classList.add('loading'); try { const resp = await fetch('/api/sync', { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (toast) { toast.textContent = data.ok ?",
|
||||
"(data.message || 'Sablonok frissítve') : ('Hiba: ' + (data.error || 'Ismeretlen hiba')); toast.className = 'sync-toast ' + (data.ok ?",
|
||||
"'sync-toast-ok' : 'sync-toast-err'); toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) { setTimeout(function() { window.location.reload(); }, 1500); } } catch (err) { if (toast) { toast.textContent = 'Hálózati hiba: ' + err.message; toast.className = 'sync-toast sync-toast-err'; toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } } btn.innerHTML = origText; btn.disabled = false; btn.classList.remove('loading'); } async function stackAction(event, name, action) { const btn = event.currentTarget; const origText = btn.textContent; btn.disabled = true; btn.textContent = 'Folyamatban...'; btn.classList.add('loading'); try { const resp = await fetch('/api/stacks/' + name + '/' + action, { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (!data.ok) { showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba')); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); return; } if (action === 'update') { followUpdate(name, btn); return; } window.location.reload(); } catch (err) { showAlert('Hálózati hiba: ' + err.message); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); } } function followUpdate(name, btn) { var started = Date.now(); var timer = setInterval(async function() { if (Date.now() - started > 30 * 60 * 1000) { clearInterval(timer); window.location.reload(); return; } try { var r = await fetch('/api/stacks/' + name); var d = await r.json(); if (!d.ok || !d.data) return; if (d.data.update_phase_label) btn.textContent = d.data.update_phase_label; if (!d.data.updating) { clearInterval(timer); window.location.reload(); } } catch (e) {} }, 3000); } async function deleteOrphanStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'delete-modal'; modal.innerHTML = ' Betöltés...",
|
||||
"'; modal.addEventListener('click', function(e) { if (e.target === modal) closeDeleteModal(); }); document.body.appendChild(modal); try { var resp = await fetch('/api/stacks/' + name + '/hdd-data'); var data = await resp.json(); var hddInfo = ''; var checkboxHTML = ''; if (data.ok && data.data && data.data.has_hdd_data) { hddInfo = ' Felhasználói adatok a merevlemezen: '; data.data.hdd_paths.forEach(function(p) { hddInfo += ' ' + p.path + ' (' + (p.exists ?",
|
||||
"' + ' Ez a művelet nem visszavonható!",
|
||||
"' + hddInfo + checkboxHTML + ' ' + ' Mégsem ' + ' Törlés ' + ' '; } catch (err) { modal.querySelector('.modal-card').innerHTML = ' Hiba Nem sikerült lekérni az adatokat: ' + err.message + ' ' + ' Bezárás '; } } function closeDeleteModal() { var modal = document.getElementById('delete-modal'); if (modal) modal.remove(); } async function confirmDelete(name) { var btn = document.getElementById('confirm-delete-btn'); var checkbox = document.getElementById('delete-hdd-check'); var removeHDD = checkbox ?",
|
||||
"checkbox.checked : false; btn.disabled = true; btn.textContent = 'Törlés folyamatban...'; try { var resp = await fetch('/api/stacks/' + name, { method: 'DELETE', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()), body: JSON.stringify({remove_hdd_data: removeHDD}) }); var data = await resp.json(); if (data.ok) { var modal = document.getElementById('delete-modal'); var removedInfo = ''; if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) { removedInfo = ' Törölt adatok: ' + data.data.hdd_paths_removed.join(', ') + ' '; } var preservedInfo = ''; if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) { preservedInfo = ' Megőrzött adatok: ' + data.data.hdd_paths_preserved.join(', ') + ' '; } if (data.data && data.data.hdd_note) { preservedInfo += ' ' + data.data.hdd_note + ' '; } modal.querySelector('.modal-card').innerHTML = ' Sikeresen törölve!",
|
||||
"' + removedInfo + preservedInfo + ' Bezárás '; } else { showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba')); btn.disabled = false; btn.textContent = 'Törlés'; } } catch (err) { showAlert('Hálózati hiba: ' + err.message); btn.disabled = false; btn.textContent = 'Törlés'; } } async function removeStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'remove-modal'; modal.innerHTML = ' Betöltés...",
|
||||
"'; modal.addEventListener('click', function(e) { if (e.target === modal) closeRemoveModal(); }); document.body.appendChild(modal); try { var [hddResp, backupResp] = await Promise.all([ fetch('/api/stacks/' + name + '/hdd-data').then(function(r) { return r.json(); }), fetch('/api/stacks/' + name + '/backup-data').then(function(r) { return r.json(); }) ]); var sections = ''; sections += ' ' + ' Mindig törlődik: ' + ' ' + ' Docker kötetek (adatbázis, alkalmazás konfiguráció) ' + ' Telepítési konfiguráció (app.yaml) ' + ' Másodlagos mentés ütemezése ' + ' '; var hddCheckbox = ''; if (hddResp.ok && hddResp.data && hddResp.data.has_hdd_data) { var hddPaths = ''; hddResp.data.hdd_paths.forEach(function(p) { hddPaths += ' ' + p.path + ' (' + (p.exists ?",
|
||||
"A megtartott adatok a továbbiakban NEM lesznek automatikusan mentve.' + ' '; hddCheckbox = '\\x3Cscript>document.getElementById(\"remove-hdd-check\").addEventListener(\"change\",function(){document.getElementById(\"remove-hdd-keep-warning\").style.display=this.checked?\"none\":\"\";}); '; } var backupCheckbox = ''; if (backupResp.ok && backupResp.data && backupResp.data.has_backups) { var bkPaths = ''; backupResp.data.backup_paths.forEach(function(p) { if (p.exists) bkPaths += ' ' + p.path + ' (' + p.size_human + ') '; }); if (bkPaths) { sections += ' ' + ' Mentési adatok: ' + bkPaths + ' Mentési adatok törlése ' + ' ' + 'Az éjszakai restic pillanatképek nem törölhetők egyenként — a megőrzési szabályok szerint automatikusan elavulnak.' + ' '; } } modal.querySelector('.modal-card').innerHTML = ' Alkalmazás eltávolítása: ' + name + ' ' + ' Az alkalmazás visszaáll \"Nincs telepítve\" állapotba."
|
||||
],
|
||||
"en": [
|
||||
"Nextcloud — Felhom.eu document.documentElement.className=document.documentElement.className.replace('no-js','js'); function csrfHeaders(){var el=document.querySelector('meta[name=\"csrf-token\"]');return el?{'X-CSRF-Token':el.content}:{};} function felhomConfirm(el,question,onYes){ if(!el||el.dataset.fcOpen)return; el.dataset.fcOpen='1'; var wrap=document.createElement('span');wrap.className='inline-confirm'; var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question; var yes=document.createElement('button');yes.type='button';yes.className='btn btn-xs btn-danger';yes.textContent='Yes'; var no=document.createElement('button');no.type='button';no.className='btn btn-xs btn-outline';no.textContent='Cancel'; wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no); el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling); function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;} no.addEventListener('click',close); yes.addEventListener('click',function(){close();onYes();}); } document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('[data-confirm]'):null; if(!btn)return; e.preventDefault(); felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){ var form=btn.closest('form'); if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();} }); }); document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null; if(!btn)return; var group=btn.closest('.nav-group'); if(!group)return; var willOpen=!group.classList.contains('is-open'); document.querySelectorAll('.nav-group.is-open').forEach(function(g){ g.classList.remove('is-open'); var t=g.querySelector('.nav-group-toggle'); if(t)t.setAttribute('aria-expanded','false'); }); if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');} }); function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML=' Message '+msg.replace(/&/g,'&').replace(/ /g,'>')+' OK ';o.appendChild(c);document.body.appendChild(o);} Launcher Dashboard Apps Storage Drives Network storage Backup Overview Remote backup Apps Restore Sharing Network sharing System monitor Debug Settings System Notifications Security and access 0.261.0 Magyar English Sign out ↗ The hub connection is off — central monitoring is not running System monitor → ← Apps Nextcloud Running Up to date Open ↗ Log Export Settings Missing storage: nextcloud This app’s data storage is not available right now, so the app is stopped.",
|
||||
"Choose a target storage… SCRATCH HDD (/mnt/felhom-drives/scratch_hdd) Move function appMigFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; } function appMigRender(job){ var names={stop:'Stopping',copy:'Copying data',verify:'Checking',flip:'Reinstalling',redeploy:'Reinstalling',cleanup:'Deleting old data'}; var s=names[job.phase]||job.phase; if(job.phase==='copy'&&job.bytes_total>0){ s+=' ('+Math.floor(100*job.bytes_done/job.bytes_total)+'% — '+appMigFmtGB(job.bytes_done)+'/'+appMigFmtGB(job.bytes_total)+')'; } return s+'…'; } function appMigWatch(){ var panel=document.getElementById('app-migrate-progress'); if(panel) panel.style.display='block'; function tick(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ var job=d.data&&d.data.job; if(!job){ if(panel) panel.textContent='No move in progress.'; return; } if(panel) panel.innerHTML=appMigRender(job); if(job.phase==='done'){ if(panel) panel.innerHTML+=' Done '; setTimeout(function(){location.reload();},1500); return; } if(job.phase==='aborted'){ if(panel) panel.innerHTML+=' Stopped: '+(job.error||'')+' The old data is untouched.'; return; } setTimeout(tick,1500); }).catch(function(){ setTimeout(tick,2000); }); } tick(); } function appMigrate(btn,app,label){ var sel=document.getElementById('app-migrate-target'); var target=sel?sel.value:''; if(!target){ showAlert('Choose a target storage.'); return; } felhomConfirm(btn,'Move the '+label+' data to: '+target+'?",
|
||||
"The old data is deleted only after a successful move.',function(){ fetch('/api/storage/migrate-app',{method:'POST',headers:Object.assign({'Content-Type':'application/json'},csrfHeaders()),body:JSON.stringify({app:app,target:target})}) .then(function(r){return r.json();}).then(function(d){ if(d.ok){ appMigWatch(); } else { showAlert('Error: '+(d.error||'unknown')); } }) .catch(function(e){ showAlert('Error: '+e); }); }); } (function(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ if(d.data&&d.data.job){ appMigWatch(); } }).catch(function(){}); })(); What is it for?",
|
||||
"Sync and share files between your devices Calendar and contact sync (CalDAV/CardDAV) Work on a document together (with the OnlyOffice integration) Photos and videos upload themselves from your phone An app store - more features when you want them (Notes, Tasks, Forms and more) First steps Open cloud.DOMAIN in your browser Sign in with the admin account you set at install time Install the Nextcloud desktop app on your computer Install the Nextcloud mobile app on your phone Invite the household by creating an account for each of them Before you start An external hard drive is needed to keep the files on At least 1 GB of free RAM (Nextcloud + MariaDB + Redis) An x86 processor is recommended for the best speed Default sign-in You set the admin account details at install time Change it right after your first sign-in!",
|
||||
"Documentation Official documentation ↗ (function(){ var burger=document.querySelector('.nav-burger'); var sidebar=document.getElementById('sidebar'); var backdrop=document.querySelector('.nav-backdrop'); if(!burger||!sidebar||!backdrop)return; function setOpen(open){ sidebar.classList.toggle('is-open',open); document.body.classList.toggle('nav-open',open); backdrop.hidden=!open; burger.setAttribute('aria-expanded',open?'true':'false'); } burger.addEventListener('click',function(){setOpen(!sidebar.classList.contains('is-open'));}); backdrop.addEventListener('click',function(){setOpen(false);}); document.addEventListener('keydown',function(e){ if(e.key==='Escape'&&sidebar.classList.contains('is-open'))setOpen(false); }); })(); document.addEventListener('click', function(e) { if (e.target.closest('a, button, .btn, input, select, textarea, .app-row-actions, .stack-detail-actions')) return; var card = e.target.closest('[data-href]'); if (card) window.location.href = card.dataset.href; }); async function checkBeforeDeploy(e, name) { try { var resp = await fetch('/api/stacks/' + name); var data = await resp.json(); if (data.ok && data.data && data.data.deployed) { e.preventDefault(); showAlert('This app is already installed.'); window.location.reload(); return false; } } catch(err) {} return true; } async function syncTemplates() { const btn = document.getElementById('sync-btn'); const toast = document.getElementById('sync-toast'); if (!btn) return; const origText = btn.innerHTML; btn.disabled = true; btn.innerHTML = '↻ Refreshing...'; btn.classList.add('loading'); try { const resp = await fetch('/api/sync', { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (toast) { toast.textContent = data.ok ?",
|
||||
"(data.message || 'Templates refreshed') : ('Error: ' + (data.error || 'Unknown error')); toast.className = 'sync-toast ' + (data.ok ?",
|
||||
"'sync-toast-ok' : 'sync-toast-err'); toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) { setTimeout(function() { window.location.reload(); }, 1500); } } catch (err) { if (toast) { toast.textContent = 'Network error: ' + err.message; toast.className = 'sync-toast sync-toast-err'; toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } } btn.innerHTML = origText; btn.disabled = false; btn.classList.remove('loading'); } async function stackAction(event, name, action) { const btn = event.currentTarget; const origText = btn.textContent; btn.disabled = true; btn.textContent = 'In progress...'; btn.classList.add('loading'); try { const resp = await fetch('/api/stacks/' + name + '/' + action, { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (!data.ok) { showAlert('Error: ' + (data.error || 'Unknown error')); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); return; } if (action === 'update') { followUpdate(name, btn); return; } window.location.reload(); } catch (err) { showAlert('Network error: ' + err.message); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); } } function followUpdate(name, btn) { var started = Date.now(); var timer = setInterval(async function() { if (Date.now() - started > 30 * 60 * 1000) { clearInterval(timer); window.location.reload(); return; } try { var r = await fetch('/api/stacks/' + name); var d = await r.json(); if (!d.ok || !d.data) return; if (d.data.update_phase_label) btn.textContent = d.data.update_phase_label; if (!d.data.updating) { clearInterval(timer); window.location.reload(); } } catch (e) {} }, 3000); } async function deleteOrphanStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'delete-modal'; modal.innerHTML = ' Loading...",
|
||||
"'; modal.addEventListener('click', function(e) { if (e.target === modal) closeDeleteModal(); }); document.body.appendChild(modal); try { var resp = await fetch('/api/stacks/' + name + '/hdd-data'); var data = await resp.json(); var hddInfo = ''; var checkboxHTML = ''; if (data.ok && data.data && data.data.has_hdd_data) { hddInfo = ' Your data on the hard drive: '; data.data.hdd_paths.forEach(function(p) { hddInfo += ' ' + p.path + ' (' + (p.exists ?",
|
||||
"' + hddInfo + checkboxHTML + ' ' + ' Cancel ' + ' Delete ' + ' '; } catch (err) { modal.querySelector('.modal-card').innerHTML = ' Error Could not load the details: ' + err.message + ' ' + ' Close '; } } function closeDeleteModal() { var modal = document.getElementById('delete-modal'); if (modal) modal.remove(); } async function confirmDelete(name) { var btn = document.getElementById('confirm-delete-btn'); var checkbox = document.getElementById('delete-hdd-check'); var removeHDD = checkbox ?",
|
||||
"checkbox.checked : false; btn.disabled = true; btn.textContent = 'Deleting...'; try { var resp = await fetch('/api/stacks/' + name, { method: 'DELETE', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()), body: JSON.stringify({remove_hdd_data: removeHDD}) }); var data = await resp.json(); if (data.ok) { var modal = document.getElementById('delete-modal'); var removedInfo = ''; if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) { removedInfo = ' Deleted data: ' + data.data.hdd_paths_removed.join(', ') + ' '; } var preservedInfo = ''; if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) { preservedInfo = ' Kept data: ' + data.data.hdd_paths_preserved.join(', ') + ' '; } if (data.data && data.data.hdd_note) { preservedInfo += ' ' + data.data.hdd_note + ' '; } modal.querySelector('.modal-card').innerHTML = ' Deleted!",
|
||||
"' + removedInfo + preservedInfo + ' Close '; } else { showAlert('Error: ' + (data.error || 'Unknown error')); btn.disabled = false; btn.textContent = 'Delete'; } } catch (err) { showAlert('Network error: ' + err.message); btn.disabled = false; btn.textContent = 'Delete'; } } async function removeStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'remove-modal'; modal.innerHTML = ' Loading...",
|
||||
"'; modal.addEventListener('click', function(e) { if (e.target === modal) closeRemoveModal(); }); document.body.appendChild(modal); try { var [hddResp, backupResp] = await Promise.all([ fetch('/api/stacks/' + name + '/hdd-data').then(function(r) { return r.json(); }), fetch('/api/stacks/' + name + '/backup-data').then(function(r) { return r.json(); }) ]); var sections = ''; sections += ' ' + ' Always deleted: ' + ' ' + ' Docker volumes (database, app configuration) ' + ' Install configuration (app.yaml) ' + ' Secondary backup schedule ' + ' '; var hddCheckbox = ''; if (hddResp.ok && hddResp.data && hddResp.data.has_hdd_data) { var hddPaths = ''; hddResp.data.hdd_paths.forEach(function(p) { hddPaths += ' ' + p.path + ' (' + (p.exists ?",
|
||||
"Data you keep is NOT backed up automatically any more.' + ' '; hddCheckbox = '\\x3Cscript>document.getElementById(\"remove-hdd-check\").addEventListener(\"change\",function(){document.getElementById(\"remove-hdd-keep-warning\").style.display=this.checked?\"none\":\"\";}); '; } var backupCheckbox = ''; if (backupResp.ok && backupResp.data && backupResp.data.has_backups) { var bkPaths = ''; backupResp.data.backup_paths.forEach(function(p) { if (p.exists) bkPaths += ' ' + p.path + ' (' + p.size_human + ') '; }); if (bkPaths) { sections += ' ' + ' Backup data: ' + bkPaths + ' Delete backup data ' + ' ' + 'The nightly restic snapshots cannot be deleted one by one — they expire automatically under the retention rules.' + ' '; } } modal.querySelector('.modal-card').innerHTML = ' Remove app: ' + name + ' ' + ' The app goes back to \"Not installed\".",
|
||||
"' + sections + ' ' + ' Cancel ' + ' Remove ' + ' ' + hddCheckbox; } catch (err) { modal.querySelector('.modal-card').innerHTML = ' Error Could not load the details: ' + err.message + ' ' + ' Close '; } } function closeRemoveModal() { var modal = document.getElementById('remove-modal'); if (modal) modal.remove(); } async function confirmRemoveStack(name) { var btn = document.getElementById('confirm-remove-btn'); var hddCheck = document.getElementById('remove-hdd-check'); var backupCheck = document.getElementById('remove-backup-check'); var removeHDD = hddCheck ?"
|
||||
]
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
+27
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"pinned_images": {
|
||||
"nextcloud": "nextcloud:34.0.1-apache",
|
||||
"nextcloud-db": "mariadb:12.3",
|
||||
"nextcloud-redis": "redis:7-alpine"
|
||||
},
|
||||
"installed_images": {
|
||||
"nextcloud": "nextcloud:34.0.1-apache",
|
||||
"nextcloud-db": "mariadb:12.3",
|
||||
"nextcloud-redis": "redis:7-alpine"
|
||||
},
|
||||
"catalog_images": {
|
||||
"nextcloud": "nextcloud:34.0.1-apache",
|
||||
"nextcloud-db": "mariadb:12.3",
|
||||
"nextcloud-redis": "redis:7-alpine"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: nextcloud:34.0.1-apache",
|
||||
"image: mariadb:12.3",
|
||||
"image: redis:7-alpine"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"nextcloud-db mariadb:12.3 running=true restarts=0",
|
||||
"nextcloud nextcloud:34.0.1-apache running=true restarts=0",
|
||||
"nextcloud-redis redis:7-alpine running=true restarts=0"
|
||||
]
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"accepted": true,
|
||||
"http": "202",
|
||||
"phases": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"phase": "safety-dump",
|
||||
"label": "Adatbázis pillanatkép…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 1.0,
|
||||
"phase": "pulling",
|
||||
"label": "Új verzió letöltése…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 3.1,
|
||||
"phase": "starting",
|
||||
"label": "Indítás az új verzióval…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 18.5,
|
||||
"phase": "verifying",
|
||||
"label": "Működés ellenőrzése…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 19.5,
|
||||
"phase": "done",
|
||||
"label": "Frissítve",
|
||||
"updating": false,
|
||||
"error": null,
|
||||
"hold": null
|
||||
}
|
||||
],
|
||||
"duration_s": 19.5,
|
||||
"final_phase": "done",
|
||||
"update_error": null,
|
||||
"hold_reason": null,
|
||||
"state": "running"
|
||||
}
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
{
|
||||
"harness_version": 1,
|
||||
"app": "nextcloud",
|
||||
"leg": "engine-major-mariadb",
|
||||
"venue": "guest 9202 demo-hp-scratch, controller 0.261.0",
|
||||
"from": {
|
||||
"nextcloud-db": "mariadb:11.6"
|
||||
},
|
||||
"to": {
|
||||
"nextcloud-db": "mariadb:12.3"
|
||||
},
|
||||
"verdict": "proven",
|
||||
"seed_read_before": true,
|
||||
"seed_read_after": true,
|
||||
"healthy_after": true,
|
||||
"migration_observed": null,
|
||||
"abort": "not-attempted",
|
||||
"abort_detail": null,
|
||||
"engine_state_before": "=== [1] mariadb_upgrade_info (the datadir's own record of which version converted it)\n11.6.2-MariaDB=== [2] the engine's OWN check — NOT the log line (R-464)\nReading datadir from the MariaDB server failed. Got the following error when executing the 'mysql' command line client\nERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)\nFATAL ERROR: Upgrade failed\nexit=1\n=== [3] the entrypoint's line, from the container log\n=== [4] the engine's own pre-upgrade backup file\n(no pre-upgrade backup file present)\n=== [5] the version actually running\nmariadbd Ver 11.6.2-MariaDB-ubu2404 for debian-linux-gnu on x86_64 (mariadb.org binary distribution)\n=== [6] container state\nimage=mariadb:11.6 running=true restarts=0 exit=0\n",
|
||||
"engine_state_after": "=== [1] mariadb_upgrade_info (the datadir's own record of which version converted it)\n12.3.3-MariaDB\n=== [2] the engine's OWN check — NOT the log line (R-464)\nReading datadir from the MariaDB server failed. Got the following error when executing the 'mysql' command line client\nERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)\nFATAL ERROR: Upgrade failed\nexit=1\n=== [3] the entrypoint's line, from the container log\n2026-09-21 21:40:23+02:00 [Note] [Entrypoint]: Starting mariadb-upgrade\nThe --upgrade-system-tables option was used, user tables won't be touched.\nMajor version upgrade detected from 11.6.2-MariaDB to 12.3.3-MariaDB. Check required!\nPhase 2/8: Installing used storage engines... Skipped\nPhase 4/8: Fixing views... Skipped\nPhase 5/8: Fixing table and database names ... Skipped\nPhase 6/8: Checking and upgrading tables... Skipped\n2026-09-21 21:40:29+02:00 [Note] [Entrypoint]: Finished mariadb-upgrade\n=== [4] the engine's own pre-upgrade backup file\n-rw-r--r-- 1 mysql mysql 631905 Sep 21 21:40 /var/lib/mysql/system_mysql_backup_11.6.2-MariaDB.sql.zst\n(no pre-upgrade backup file present)\n=== [5] the version actually running\nmariadbd Ver 12.3.3-MariaDB-ubu2404 for debian-linux-gnu on x86_64 (mariadb.org binary distribution)\n=== [6] container state\nimage=mariadb:12.3 running=true restarts=0 exit=0\n",
|
||||
"duration_s": 217.4,
|
||||
"measured_at": "2026-09-21T19:37:10.235635+00:00",
|
||||
"evidence": "apps/nextcloud-engine-mariadb/",
|
||||
"notes": [],
|
||||
"badge_catchup_seconds": 29.0,
|
||||
"phases": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"phase": "safety-dump",
|
||||
"label": "Adatbázis pillanatkép…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 1.0,
|
||||
"phase": "pulling",
|
||||
"label": "Új verzió letöltése…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 3.1,
|
||||
"phase": "starting",
|
||||
"label": "Indítás az új verzióval…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 18.5,
|
||||
"phase": "verifying",
|
||||
"label": "Működés ellenőrzése…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 19.5,
|
||||
"phase": "done",
|
||||
"label": "Frissítve",
|
||||
"updating": false,
|
||||
"error": null,
|
||||
"hold": null
|
||||
}
|
||||
],
|
||||
"final_phase": "done",
|
||||
"hold_reason": null,
|
||||
"update_error": null,
|
||||
"hold_sentences": {
|
||||
"hu": [
|
||||
"Nextcloud — Felhom.eu document.documentElement.className=document.documentElement.className.replace('no-js','js'); function csrfHeaders(){var el=document.querySelector('meta[name=\"csrf-token\"]');return el?{'X-CSRF-Token':el.content}:{};} function felhomConfirm(el,question,onYes){ if(!el||el.dataset.fcOpen)return; el.dataset.fcOpen='1'; var wrap=document.createElement('span');wrap.className='inline-confirm'; var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question; var yes=document.createElement('button');yes.type='button';yes.className='btn btn-xs btn-danger';yes.textContent='Igen'; var no=document.createElement('button');no.type='button';no.className='btn btn-xs btn-outline';no.textContent='Mégse'; wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no); el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling); function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;} no.addEventListener('click',close); yes.addEventListener('click',function(){close();onYes();}); } document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('[data-confirm]'):null; if(!btn)return; e.preventDefault(); felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){ var form=btn.closest('form'); if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();} }); }); document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null; if(!btn)return; var group=btn.closest('.nav-group'); if(!group)return; var willOpen=!group.classList.contains('is-open'); document.querySelectorAll('.nav-group.is-open').forEach(function(g){ g.classList.remove('is-open'); var t=g.querySelector('.nav-group-toggle'); if(t)t.setAttribute('aria-expanded','false'); }); if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');} }); function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML=' Üzenet '+msg.replace(/&/g,'&').replace(/ /g,'>')+' OK ';o.appendChild(c);document.body.appendChild(o);} Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások Nextcloud Fut Naprakész Megnyitás ↗ Napló Exportálás Beállítások Hiányzó tárhely: nextcloud Ennek az alkalmazásnak az adattárolója jelenleg nem elérhető, ezért le van állítva.",
|
||||
"Válassz céltárhelyet… SCRATCH HDD (/mnt/felhom-drives/scratch_hdd) Áthelyezés function appMigFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; } function appMigRender(job){ var names={stop:'Leállítás',copy:'Adatok másolása',verify:'Ellenőrzés',flip:'Újratelepítés',redeploy:'Újratelepítés',cleanup:'Régi adatok törlése'}; var s=names[job.phase]||job.phase; if(job.phase==='copy'&&job.bytes_total>0){ s+=' ('+Math.floor(100*job.bytes_done/job.bytes_total)+'% — '+appMigFmtGB(job.bytes_done)+'/'+appMigFmtGB(job.bytes_total)+')'; } return s+'…'; } function appMigWatch(){ var panel=document.getElementById('app-migrate-progress'); if(panel) panel.style.display='block'; function tick(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ var job=d.data&&d.data.job; if(!job){ if(panel) panel.textContent='Nincs folyamatban áthelyezés.'; return; } if(panel) panel.innerHTML=appMigRender(job); if(job.phase==='done'){ if(panel) panel.innerHTML+=' Kész '; setTimeout(function(){location.reload();},1500); return; } if(job.phase==='aborted'){ if(panel) panel.innerHTML+=' Megszakadt: '+(job.error||'')+' A régi adatok érintetlenek.'; return; } setTimeout(tick,1500); }).catch(function(){ setTimeout(tick,2000); }); } tick(); } function appMigrate(btn,app,label){ var sel=document.getElementById('app-migrate-target'); var target=sel?sel.value:''; if(!target){ showAlert('Válassz céltárhelyet.'); return; } felhomConfirm(btn,'Áthelyezed a(z) '+label+' adatait ide: '+target+'?",
|
||||
"A régi adatok csak sikeres áthelyezés után törlődnek.',function(){ fetch('/api/storage/migrate-app',{method:'POST',headers:Object.assign({'Content-Type':'application/json'},csrfHeaders()),body:JSON.stringify({app:app,target:target})}) .then(function(r){return r.json();}).then(function(d){ if(d.ok){ appMigWatch(); } else { showAlert('Hiba: '+(d.error||'ismeretlen')); } }) .catch(function(e){ showAlert('Hiba: '+e); }); }); } (function(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ if(d.data&&d.data.job){ appMigWatch(); } }).catch(function(){}); })(); Mire használható?",
|
||||
"Fájlok szinkronizálása és megosztása eszközök között Naptár és névjegyek szinkronizálás (CalDAV/CardDAV) Dokumentumok közös szerkesztése (OnlyOffice integrációval) Fotó és videó automatikus feltöltés telefonról Alkalmazásbolt - kibővíthető funkciók (Notes, Tasks, Forms, stb.) Első lépések Nyisd meg a cloud.DOMAIN címet a böngészőben Jelentkezz be a telepítéskor megadott admin fiókkal Telepítsd a Nextcloud asztali alkalmazást a számítógépedre Telepítsd a Nextcloud mobil alkalmazást a telefonodra Hívd meg a családtagokat felhasználói fiókok létrehozásával Előfeltételek Külső HDD szükséges a fájlok tárolásához Legalább 1 GB szabad RAM (Nextcloud + MariaDB + Redis) x86 processzor ajánlott a legjobb teljesítményhez Alapértelmezett belépés Az admin fiók adatait a telepítéskor adod meg Az első bejelentkezés után azonnal változtasd meg!",
|
||||
"Dokumentáció Hivatalos dokumentáció ↗ (function(){ var burger=document.querySelector('.nav-burger'); var sidebar=document.getElementById('sidebar'); var backdrop=document.querySelector('.nav-backdrop'); if(!burger||!sidebar||!backdrop)return; function setOpen(open){ sidebar.classList.toggle('is-open',open); document.body.classList.toggle('nav-open',open); backdrop.hidden=!open; burger.setAttribute('aria-expanded',open?'true':'false'); } burger.addEventListener('click',function(){setOpen(!sidebar.classList.contains('is-open'));}); backdrop.addEventListener('click',function(){setOpen(false);}); document.addEventListener('keydown',function(e){ if(e.key==='Escape'&&sidebar.classList.contains('is-open'))setOpen(false); }); })(); document.addEventListener('click', function(e) { if (e.target.closest('a, button, .btn, input, select, textarea, .app-row-actions, .stack-detail-actions')) return; var card = e.target.closest('[data-href]'); if (card) window.location.href = card.dataset.href; }); async function checkBeforeDeploy(e, name) { try { var resp = await fetch('/api/stacks/' + name); var data = await resp.json(); if (data.ok && data.data && data.data.deployed) { e.preventDefault(); showAlert('Ez az alkalmazás már telepítve van.'); window.location.reload(); return false; } } catch(err) {} return true; } async function syncTemplates() { const btn = document.getElementById('sync-btn'); const toast = document.getElementById('sync-toast'); if (!btn) return; const origText = btn.innerHTML; btn.disabled = true; btn.innerHTML = '↻ Frissítés...'; btn.classList.add('loading'); try { const resp = await fetch('/api/sync', { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (toast) { toast.textContent = data.ok ?",
|
||||
"(data.message || 'Sablonok frissítve') : ('Hiba: ' + (data.error || 'Ismeretlen hiba')); toast.className = 'sync-toast ' + (data.ok ?",
|
||||
"'sync-toast-ok' : 'sync-toast-err'); toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) { setTimeout(function() { window.location.reload(); }, 1500); } } catch (err) { if (toast) { toast.textContent = 'Hálózati hiba: ' + err.message; toast.className = 'sync-toast sync-toast-err'; toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } } btn.innerHTML = origText; btn.disabled = false; btn.classList.remove('loading'); } async function stackAction(event, name, action) { const btn = event.currentTarget; const origText = btn.textContent; btn.disabled = true; btn.textContent = 'Folyamatban...'; btn.classList.add('loading'); try { const resp = await fetch('/api/stacks/' + name + '/' + action, { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (!data.ok) { showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba')); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); return; } if (action === 'update') { followUpdate(name, btn); return; } window.location.reload(); } catch (err) { showAlert('Hálózati hiba: ' + err.message); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); } } function followUpdate(name, btn) { var started = Date.now(); var timer = setInterval(async function() { if (Date.now() - started > 30 * 60 * 1000) { clearInterval(timer); window.location.reload(); return; } try { var r = await fetch('/api/stacks/' + name); var d = await r.json(); if (!d.ok || !d.data) return; if (d.data.update_phase_label) btn.textContent = d.data.update_phase_label; if (!d.data.updating) { clearInterval(timer); window.location.reload(); } } catch (e) {} }, 3000); } async function deleteOrphanStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'delete-modal'; modal.innerHTML = ' Betöltés...",
|
||||
"'; modal.addEventListener('click', function(e) { if (e.target === modal) closeDeleteModal(); }); document.body.appendChild(modal); try { var resp = await fetch('/api/stacks/' + name + '/hdd-data'); var data = await resp.json(); var hddInfo = ''; var checkboxHTML = ''; if (data.ok && data.data && data.data.has_hdd_data) { hddInfo = ' Felhasználói adatok a merevlemezen: '; data.data.hdd_paths.forEach(function(p) { hddInfo += ' ' + p.path + ' (' + (p.exists ?",
|
||||
"' + ' Ez a művelet nem visszavonható!",
|
||||
"' + hddInfo + checkboxHTML + ' ' + ' Mégsem ' + ' Törlés ' + ' '; } catch (err) { modal.querySelector('.modal-card').innerHTML = ' Hiba Nem sikerült lekérni az adatokat: ' + err.message + ' ' + ' Bezárás '; } } function closeDeleteModal() { var modal = document.getElementById('delete-modal'); if (modal) modal.remove(); } async function confirmDelete(name) { var btn = document.getElementById('confirm-delete-btn'); var checkbox = document.getElementById('delete-hdd-check'); var removeHDD = checkbox ?",
|
||||
"checkbox.checked : false; btn.disabled = true; btn.textContent = 'Törlés folyamatban...'; try { var resp = await fetch('/api/stacks/' + name, { method: 'DELETE', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()), body: JSON.stringify({remove_hdd_data: removeHDD}) }); var data = await resp.json(); if (data.ok) { var modal = document.getElementById('delete-modal'); var removedInfo = ''; if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) { removedInfo = ' Törölt adatok: ' + data.data.hdd_paths_removed.join(', ') + ' '; } var preservedInfo = ''; if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) { preservedInfo = ' Megőrzött adatok: ' + data.data.hdd_paths_preserved.join(', ') + ' '; } if (data.data && data.data.hdd_note) { preservedInfo += ' ' + data.data.hdd_note + ' '; } modal.querySelector('.modal-card').innerHTML = ' Sikeresen törölve!",
|
||||
"' + removedInfo + preservedInfo + ' Bezárás '; } else { showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba')); btn.disabled = false; btn.textContent = 'Törlés'; } } catch (err) { showAlert('Hálózati hiba: ' + err.message); btn.disabled = false; btn.textContent = 'Törlés'; } } async function removeStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'remove-modal'; modal.innerHTML = ' Betöltés...",
|
||||
"'; modal.addEventListener('click', function(e) { if (e.target === modal) closeRemoveModal(); }); document.body.appendChild(modal); try { var [hddResp, backupResp] = await Promise.all([ fetch('/api/stacks/' + name + '/hdd-data').then(function(r) { return r.json(); }), fetch('/api/stacks/' + name + '/backup-data').then(function(r) { return r.json(); }) ]); var sections = ''; sections += ' ' + ' Mindig törlődik: ' + ' ' + ' Docker kötetek (adatbázis, alkalmazás konfiguráció) ' + ' Telepítési konfiguráció (app.yaml) ' + ' Másodlagos mentés ütemezése ' + ' '; var hddCheckbox = ''; if (hddResp.ok && hddResp.data && hddResp.data.has_hdd_data) { var hddPaths = ''; hddResp.data.hdd_paths.forEach(function(p) { hddPaths += ' ' + p.path + ' (' + (p.exists ?",
|
||||
"A megtartott adatok a továbbiakban NEM lesznek automatikusan mentve.' + ' '; hddCheckbox = '\\x3Cscript>document.getElementById(\"remove-hdd-check\").addEventListener(\"change\",function(){document.getElementById(\"remove-hdd-keep-warning\").style.display=this.checked?\"none\":\"\";}); '; } var backupCheckbox = ''; if (backupResp.ok && backupResp.data && backupResp.data.has_backups) { var bkPaths = ''; backupResp.data.backup_paths.forEach(function(p) { if (p.exists) bkPaths += ' ' + p.path + ' (' + p.size_human + ') '; }); if (bkPaths) { sections += ' ' + ' Mentési adatok: ' + bkPaths + ' Mentési adatok törlése ' + ' ' + 'Az éjszakai restic pillanatképek nem törölhetők egyenként — a megőrzési szabályok szerint automatikusan elavulnak.' + ' '; } } modal.querySelector('.modal-card').innerHTML = ' Alkalmazás eltávolítása: ' + name + ' ' + ' Az alkalmazás visszaáll \"Nincs telepítve\" állapotba."
|
||||
],
|
||||
"en": [
|
||||
"Nextcloud — Felhom.eu document.documentElement.className=document.documentElement.className.replace('no-js','js'); function csrfHeaders(){var el=document.querySelector('meta[name=\"csrf-token\"]');return el?{'X-CSRF-Token':el.content}:{};} function felhomConfirm(el,question,onYes){ if(!el||el.dataset.fcOpen)return; el.dataset.fcOpen='1'; var wrap=document.createElement('span');wrap.className='inline-confirm'; var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question; var yes=document.createElement('button');yes.type='button';yes.className='btn btn-xs btn-danger';yes.textContent='Yes'; var no=document.createElement('button');no.type='button';no.className='btn btn-xs btn-outline';no.textContent='Cancel'; wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no); el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling); function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;} no.addEventListener('click',close); yes.addEventListener('click',function(){close();onYes();}); } document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('[data-confirm]'):null; if(!btn)return; e.preventDefault(); felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){ var form=btn.closest('form'); if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();} }); }); document.addEventListener('click',function(e){ var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null; if(!btn)return; var group=btn.closest('.nav-group'); if(!group)return; var willOpen=!group.classList.contains('is-open'); document.querySelectorAll('.nav-group.is-open').forEach(function(g){ g.classList.remove('is-open'); var t=g.querySelector('.nav-group-toggle'); if(t)t.setAttribute('aria-expanded','false'); }); if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');} }); function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML=' Message '+msg.replace(/&/g,'&').replace(/ /g,'>')+' OK ';o.appendChild(c);document.body.appendChild(o);} Launcher Dashboard Apps Storage Drives Network storage Backup Overview Remote backup Apps Restore Sharing Network sharing System monitor Debug Settings System Notifications Security and access 0.261.0 Magyar English Sign out ↗ The hub connection is off — central monitoring is not running System monitor → ← Apps Nextcloud Running Up to date Open ↗ Log Export Settings Missing storage: nextcloud This app’s data storage is not available right now, so the app is stopped.",
|
||||
"Choose a target storage… SCRATCH HDD (/mnt/felhom-drives/scratch_hdd) Move function appMigFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; } function appMigRender(job){ var names={stop:'Stopping',copy:'Copying data',verify:'Checking',flip:'Reinstalling',redeploy:'Reinstalling',cleanup:'Deleting old data'}; var s=names[job.phase]||job.phase; if(job.phase==='copy'&&job.bytes_total>0){ s+=' ('+Math.floor(100*job.bytes_done/job.bytes_total)+'% — '+appMigFmtGB(job.bytes_done)+'/'+appMigFmtGB(job.bytes_total)+')'; } return s+'…'; } function appMigWatch(){ var panel=document.getElementById('app-migrate-progress'); if(panel) panel.style.display='block'; function tick(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ var job=d.data&&d.data.job; if(!job){ if(panel) panel.textContent='No move in progress.'; return; } if(panel) panel.innerHTML=appMigRender(job); if(job.phase==='done'){ if(panel) panel.innerHTML+=' Done '; setTimeout(function(){location.reload();},1500); return; } if(job.phase==='aborted'){ if(panel) panel.innerHTML+=' Stopped: '+(job.error||'')+' The old data is untouched.'; return; } setTimeout(tick,1500); }).catch(function(){ setTimeout(tick,2000); }); } tick(); } function appMigrate(btn,app,label){ var sel=document.getElementById('app-migrate-target'); var target=sel?sel.value:''; if(!target){ showAlert('Choose a target storage.'); return; } felhomConfirm(btn,'Move the '+label+' data to: '+target+'?",
|
||||
"The old data is deleted only after a successful move.',function(){ fetch('/api/storage/migrate-app',{method:'POST',headers:Object.assign({'Content-Type':'application/json'},csrfHeaders()),body:JSON.stringify({app:app,target:target})}) .then(function(r){return r.json();}).then(function(d){ if(d.ok){ appMigWatch(); } else { showAlert('Error: '+(d.error||'unknown')); } }) .catch(function(e){ showAlert('Error: '+e); }); }); } (function(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ if(d.data&&d.data.job){ appMigWatch(); } }).catch(function(){}); })(); What is it for?",
|
||||
"Sync and share files between your devices Calendar and contact sync (CalDAV/CardDAV) Work on a document together (with the OnlyOffice integration) Photos and videos upload themselves from your phone An app store - more features when you want them (Notes, Tasks, Forms and more) First steps Open cloud.DOMAIN in your browser Sign in with the admin account you set at install time Install the Nextcloud desktop app on your computer Install the Nextcloud mobile app on your phone Invite the household by creating an account for each of them Before you start An external hard drive is needed to keep the files on At least 1 GB of free RAM (Nextcloud + MariaDB + Redis) An x86 processor is recommended for the best speed Default sign-in You set the admin account details at install time Change it right after your first sign-in!",
|
||||
"Documentation Official documentation ↗ (function(){ var burger=document.querySelector('.nav-burger'); var sidebar=document.getElementById('sidebar'); var backdrop=document.querySelector('.nav-backdrop'); if(!burger||!sidebar||!backdrop)return; function setOpen(open){ sidebar.classList.toggle('is-open',open); document.body.classList.toggle('nav-open',open); backdrop.hidden=!open; burger.setAttribute('aria-expanded',open?'true':'false'); } burger.addEventListener('click',function(){setOpen(!sidebar.classList.contains('is-open'));}); backdrop.addEventListener('click',function(){setOpen(false);}); document.addEventListener('keydown',function(e){ if(e.key==='Escape'&&sidebar.classList.contains('is-open'))setOpen(false); }); })(); document.addEventListener('click', function(e) { if (e.target.closest('a, button, .btn, input, select, textarea, .app-row-actions, .stack-detail-actions')) return; var card = e.target.closest('[data-href]'); if (card) window.location.href = card.dataset.href; }); async function checkBeforeDeploy(e, name) { try { var resp = await fetch('/api/stacks/' + name); var data = await resp.json(); if (data.ok && data.data && data.data.deployed) { e.preventDefault(); showAlert('This app is already installed.'); window.location.reload(); return false; } } catch(err) {} return true; } async function syncTemplates() { const btn = document.getElementById('sync-btn'); const toast = document.getElementById('sync-toast'); if (!btn) return; const origText = btn.innerHTML; btn.disabled = true; btn.innerHTML = '↻ Refreshing...'; btn.classList.add('loading'); try { const resp = await fetch('/api/sync', { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (toast) { toast.textContent = data.ok ?",
|
||||
"(data.message || 'Templates refreshed') : ('Error: ' + (data.error || 'Unknown error')); toast.className = 'sync-toast ' + (data.ok ?",
|
||||
"'sync-toast-ok' : 'sync-toast-err'); toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) { setTimeout(function() { window.location.reload(); }, 1500); } } catch (err) { if (toast) { toast.textContent = 'Network error: ' + err.message; toast.className = 'sync-toast sync-toast-err'; toast.style.display = 'block'; setTimeout(function() { toast.style.display = 'none'; }, 5000); } } btn.innerHTML = origText; btn.disabled = false; btn.classList.remove('loading'); } async function stackAction(event, name, action) { const btn = event.currentTarget; const origText = btn.textContent; btn.disabled = true; btn.textContent = 'In progress...'; btn.classList.add('loading'); try { const resp = await fetch('/api/stacks/' + name + '/' + action, { method: 'POST', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()) }); const data = await resp.json(); if (!data.ok) { showAlert('Error: ' + (data.error || 'Unknown error')); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); return; } if (action === 'update') { followUpdate(name, btn); return; } window.location.reload(); } catch (err) { showAlert('Network error: ' + err.message); btn.textContent = origText; btn.disabled = false; btn.classList.remove('loading'); } } function followUpdate(name, btn) { var started = Date.now(); var timer = setInterval(async function() { if (Date.now() - started > 30 * 60 * 1000) { clearInterval(timer); window.location.reload(); return; } try { var r = await fetch('/api/stacks/' + name); var d = await r.json(); if (!d.ok || !d.data) return; if (d.data.update_phase_label) btn.textContent = d.data.update_phase_label; if (!d.data.updating) { clearInterval(timer); window.location.reload(); } } catch (e) {} }, 3000); } async function deleteOrphanStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'delete-modal'; modal.innerHTML = ' Loading...",
|
||||
"'; modal.addEventListener('click', function(e) { if (e.target === modal) closeDeleteModal(); }); document.body.appendChild(modal); try { var resp = await fetch('/api/stacks/' + name + '/hdd-data'); var data = await resp.json(); var hddInfo = ''; var checkboxHTML = ''; if (data.ok && data.data && data.data.has_hdd_data) { hddInfo = ' Your data on the hard drive: '; data.data.hdd_paths.forEach(function(p) { hddInfo += ' ' + p.path + ' (' + (p.exists ?",
|
||||
"' + hddInfo + checkboxHTML + ' ' + ' Cancel ' + ' Delete ' + ' '; } catch (err) { modal.querySelector('.modal-card').innerHTML = ' Error Could not load the details: ' + err.message + ' ' + ' Close '; } } function closeDeleteModal() { var modal = document.getElementById('delete-modal'); if (modal) modal.remove(); } async function confirmDelete(name) { var btn = document.getElementById('confirm-delete-btn'); var checkbox = document.getElementById('delete-hdd-check'); var removeHDD = checkbox ?",
|
||||
"checkbox.checked : false; btn.disabled = true; btn.textContent = 'Deleting...'; try { var resp = await fetch('/api/stacks/' + name, { method: 'DELETE', headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()), body: JSON.stringify({remove_hdd_data: removeHDD}) }); var data = await resp.json(); if (data.ok) { var modal = document.getElementById('delete-modal'); var removedInfo = ''; if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) { removedInfo = ' Deleted data: ' + data.data.hdd_paths_removed.join(', ') + ' '; } var preservedInfo = ''; if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) { preservedInfo = ' Kept data: ' + data.data.hdd_paths_preserved.join(', ') + ' '; } if (data.data && data.data.hdd_note) { preservedInfo += ' ' + data.data.hdd_note + ' '; } modal.querySelector('.modal-card').innerHTML = ' Deleted!",
|
||||
"' + removedInfo + preservedInfo + ' Close '; } else { showAlert('Error: ' + (data.error || 'Unknown error')); btn.disabled = false; btn.textContent = 'Delete'; } } catch (err) { showAlert('Network error: ' + err.message); btn.disabled = false; btn.textContent = 'Delete'; } } async function removeStack(name) { var modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.id = 'remove-modal'; modal.innerHTML = ' Loading...",
|
||||
"'; modal.addEventListener('click', function(e) { if (e.target === modal) closeRemoveModal(); }); document.body.appendChild(modal); try { var [hddResp, backupResp] = await Promise.all([ fetch('/api/stacks/' + name + '/hdd-data').then(function(r) { return r.json(); }), fetch('/api/stacks/' + name + '/backup-data').then(function(r) { return r.json(); }) ]); var sections = ''; sections += ' ' + ' Always deleted: ' + ' ' + ' Docker volumes (database, app configuration) ' + ' Install configuration (app.yaml) ' + ' Secondary backup schedule ' + ' '; var hddCheckbox = ''; if (hddResp.ok && hddResp.data && hddResp.data.has_hdd_data) { var hddPaths = ''; hddResp.data.hdd_paths.forEach(function(p) { hddPaths += ' ' + p.path + ' (' + (p.exists ?",
|
||||
"Data you keep is NOT backed up automatically any more.' + ' '; hddCheckbox = '\\x3Cscript>document.getElementById(\"remove-hdd-check\").addEventListener(\"change\",function(){document.getElementById(\"remove-hdd-keep-warning\").style.display=this.checked?\"none\":\"\";}); '; } var backupCheckbox = ''; if (backupResp.ok && backupResp.data && backupResp.data.has_backups) { var bkPaths = ''; backupResp.data.backup_paths.forEach(function(p) { if (p.exists) bkPaths += ' ' + p.path + ' (' + p.size_human + ') '; }); if (bkPaths) { sections += ' ' + ' Backup data: ' + bkPaths + ' Delete backup data ' + ' ' + 'The nightly restic snapshots cannot be deleted one by one — they expire automatically under the retention rules.' + ' '; } } modal.querySelector('.modal-card').innerHTML = ' Remove app: ' + name + ' ' + ' The app goes back to \"Not installed\".",
|
||||
"' + sections + ' ' + ' Cancel ' + ' Remove ' + ' ' + hddCheckbox; } catch (err) { modal.querySelector('.modal-card').innerHTML = ' Error Could not load the details: ' + err.message + ' ' + ' Close '; } } function closeRemoveModal() { var modal = document.getElementById('remove-modal'); if (modal) modal.remove(); } async function confirmRemoveStack(name) { var btn = document.getElementById('confirm-remove-btn'); var hddCheck = document.getElementById('remove-hdd-check'); var backupCheck = document.getElementById('remove-backup-check'); var removeHDD = hddCheck ?"
|
||||
]
|
||||
},
|
||||
"observables_after": {
|
||||
"pinned_images": {
|
||||
"nextcloud": "nextcloud:34.0.1-apache",
|
||||
"nextcloud-db": "mariadb:12.3",
|
||||
"nextcloud-redis": "redis:7-alpine"
|
||||
},
|
||||
"installed_images": {
|
||||
"nextcloud": "nextcloud:34.0.1-apache",
|
||||
"nextcloud-db": "mariadb:12.3",
|
||||
"nextcloud-redis": "redis:7-alpine"
|
||||
},
|
||||
"catalog_images": {
|
||||
"nextcloud": "nextcloud:34.0.1-apache",
|
||||
"nextcloud-db": "mariadb:12.3",
|
||||
"nextcloud-redis": "redis:7-alpine"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: nextcloud:34.0.1-apache",
|
||||
"image: mariadb:12.3",
|
||||
"image: redis:7-alpine"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"nextcloud-db mariadb:12.3 running=true restarts=0",
|
||||
"nextcloud nextcloud:34.0.1-apache running=true restarts=0",
|
||||
"nextcloud-redis redis:7-alpine running=true restarts=0"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
opengist | Opengist v1.15.2
|
||||
opengist | Using YAML config file: /config.yml
|
||||
opengist | Using environment variables config: OG_EXTERNAL_URL, OG_OPENGIST_HOME
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"before": {
|
||||
"hu": [
|
||||
{
|
||||
"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.",
|
||||
"text": "Naprakész"
|
||||
}
|
||||
],
|
||||
"en": [
|
||||
{
|
||||
"title": "This app is running the newest version available.",
|
||||
"text": "Up to date"
|
||||
}
|
||||
]
|
||||
},
|
||||
"after": {
|
||||
"hu": [
|
||||
{
|
||||
"title": "Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.",
|
||||
"text": "Frissítés elérhető — ma"
|
||||
}
|
||||
],
|
||||
"en": [
|
||||
{
|
||||
"title": "A newer version of this app is available. Select the Update button to start it.",
|
||||
"text": "Update available — today"
|
||||
}
|
||||
]
|
||||
},
|
||||
"drill_commit": "d22af261ca40"
|
||||
}
|
||||
@@ -1,18 +1,25 @@
|
||||
21:05:28 ==== opengist: ghcr.io/thomiceli/opengist:1.13 -> ghcr.io/thomiceli/opengist:1.15 (sub=gist, class=other)
|
||||
21:05:29 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
|
||||
21:05:44 [1] deployed, controller state=running, pinned={'opengist': 'ghcr.io/thomiceli/opengist:1.13'}
|
||||
21:05:46 [2] seeding through the app's own front door
|
||||
21:05:46 opengist: /register http=500
|
||||
21:05:46 opengist: refused
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="h-full " data-theme="auto">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
||||
|
||||
<base href="https://gist.enkisfelhom.hu" />
|
||||
|
||||
<script>
|
||||
window.opengist_bas
|
||||
21:05:46 [9] verdict inconclusive -> /mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/update-night-2026-09-21/apps/opengist/verdict.json
|
||||
21:33:06 ==== opengist: ghcr.io/thomiceli/opengist:1.13 -> ghcr.io/thomiceli/opengist:1.15 (sub=gist, class=other)
|
||||
21:33:07 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
|
||||
21:33:17 [1] deployed, controller state=running, pinned={'opengist': 'ghcr.io/thomiceli/opengist:1.13'}
|
||||
21:33:19 [2] seeding through the app's own front door
|
||||
21:33:19 opengist: /register (with its own _csrf) http=302
|
||||
21:33:19 [3] control C1 — reading the seed back BEFORE the update
|
||||
21:33:21 opengist: sign-in as the seeded account http=302 name_on_page=True
|
||||
21:33:21 [4] „Mentés most" -> 200 {'ok': True, 'message': 'Mentés elindítva'}
|
||||
21:34:06 [4] backup idle; last=None
|
||||
21:34:07 [5] drill commit d22af261ca40: opengist ghcr.io/thomiceli/opengist:1.13 -> ghcr.io/thomiceli/opengist:1.15 (push rc=0)
|
||||
21:34:11 [5] badge HU: [{'title': 'Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.', 'text': 'Frissítés elérhető — ma'}]
|
||||
21:34:11 [5] badge EN: [{'title': 'A newer version of this app is available. Select the Update button to start it.', 'text': 'Update available — today'}]
|
||||
21:34:12 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}
|
||||
21:34:12 + 0.0s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None
|
||||
21:34:13 + 1.0s phase=pulling label=Új verzió letöltése… err=None hold=None
|
||||
21:34:20 + 8.2s phase=starting label=Indítás az új verzióval… err=None hold=None
|
||||
21:34:21 + 9.2s phase=verifying label=Működés ellenőrzése… err=None hold=None
|
||||
21:34:26 + 14.4s phase=done label=Frissítve err=None hold=None
|
||||
21:34:26 [7] reading the seed back AFTER the update
|
||||
21:34:26 opengist: sign-in as the seeded account http=None name_on_page=False
|
||||
21:34:29 [8] pinned = {'opengist': 'ghcr.io/thomiceli/opengist:1.15'}
|
||||
21:34:29 [8] installed = {'opengist': 'ghcr.io/thomiceli/opengist:1.15'}
|
||||
21:34:29 [8] compose = ['image: ghcr.io/thomiceli/opengist:1.15']
|
||||
21:34:29 [8] inspect = ['opengist ghcr.io/thomiceli/opengist:1.15 running=true restarts=0']
|
||||
21:34:29 [9] verdict failed -> /mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/update-night-2026-09-21/apps/opengist/verdict.json
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
"installed_images": {
|
||||
"opengist": "ghcr.io/thomiceli/opengist:1.13"
|
||||
},
|
||||
"catalog_images": {
|
||||
"opengist": "ghcr.io/thomiceli/opengist:1.13"
|
||||
},
|
||||
"catalog_images": null,
|
||||
"live_compose_image_lines": [
|
||||
"image: ghcr.io/thomiceli/opengist:1.13"
|
||||
],
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"before": {
|
||||
"pinned_images": {
|
||||
"opengist": "ghcr.io/thomiceli/opengist:1.13"
|
||||
},
|
||||
"installed_images": {
|
||||
"opengist": "ghcr.io/thomiceli/opengist:1.13"
|
||||
},
|
||||
"catalog_images": null,
|
||||
"live_compose_image_lines": [
|
||||
"image: ghcr.io/thomiceli/opengist:1.13"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"opengist ghcr.io/thomiceli/opengist:1.13 running=true restarts=0"
|
||||
]
|
||||
},
|
||||
"after": {
|
||||
"pinned_images": {
|
||||
"opengist": "ghcr.io/thomiceli/opengist:1.15"
|
||||
},
|
||||
"installed_images": {
|
||||
"opengist": "ghcr.io/thomiceli/opengist:1.15"
|
||||
},
|
||||
"catalog_images": {
|
||||
"opengist": "ghcr.io/thomiceli/opengist:1.15"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: ghcr.io/thomiceli/opengist:1.15"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"opengist ghcr.io/thomiceli/opengist:1.15 running=true restarts=0"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"accepted": true,
|
||||
"http": "202",
|
||||
"phases": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"phase": "safety-dump",
|
||||
"label": "Adatbázis pillanatkép…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 1.0,
|
||||
"phase": "pulling",
|
||||
"label": "Új verzió letöltése…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 8.2,
|
||||
"phase": "starting",
|
||||
"label": "Indítás az új verzióval…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 9.2,
|
||||
"phase": "verifying",
|
||||
"label": "Működés ellenőrzése…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 14.4,
|
||||
"phase": "done",
|
||||
"label": "Frissítve",
|
||||
"updating": false,
|
||||
"error": null,
|
||||
"hold": null
|
||||
}
|
||||
],
|
||||
"duration_s": 14.4,
|
||||
"final_phase": "done",
|
||||
"update_error": null,
|
||||
"hold_reason": null,
|
||||
"state": "running"
|
||||
}
|
||||
@@ -6,19 +6,41 @@
|
||||
"from": {
|
||||
"opengist": "ghcr.io/thomiceli/opengist:1.13"
|
||||
},
|
||||
"to": {},
|
||||
"to": {
|
||||
"opengist": "ghcr.io/thomiceli/opengist:1.15"
|
||||
},
|
||||
"verdict": "inconclusive",
|
||||
"seed_read_before": false,
|
||||
"seed_read_before": true,
|
||||
"seed_read_after": false,
|
||||
"healthy_after": false,
|
||||
"healthy_after": true,
|
||||
"migration_observed": null,
|
||||
"abort": "not-attempted",
|
||||
"abort_detail": null,
|
||||
"duration_s": 17.9,
|
||||
"measured_at": "2026-09-21T19:05:29.084151+00:00",
|
||||
"duration_s": 14.4,
|
||||
"measured_at": "2026-09-21T19:33:06.999531+00:00",
|
||||
"evidence": "apps/opengist/",
|
||||
"notes": [
|
||||
"seed refused through the app's own route — see log.txt for what was tried",
|
||||
"seed route did not work tonight"
|
||||
]
|
||||
"CORRECTED from `failed` to `inconclusive` the same night, deliberately. The UPDATE itself SUCCEEDED: phase `done` in 14.4 s, and all four version observables agree on `ghcr.io/thomiceli/opengist:1.15` with the container running and zero restarts. What failed was the READBACK: it was attempted immediately after `done` and the sign-in form was not yet being served, so the fixture got no `_csrf` and returned `http=None`. Whether the seeded account survived was therefore NOT ESTABLISHED. Recording that as `failed` would have blamed the app for the harness's impatience — `inconclusive` is the honest verdict and it is never collapsed into `failed`. The fixture now waits for the LOGIN FORM rather than for the root page."
|
||||
],
|
||||
"badge_catchup_seconds": 4.5,
|
||||
"observables_after": {
|
||||
"pinned_images": {
|
||||
"opengist": "ghcr.io/thomiceli/opengist:1.15"
|
||||
},
|
||||
"installed_images": {
|
||||
"opengist": "ghcr.io/thomiceli/opengist:1.15"
|
||||
},
|
||||
"catalog_images": {
|
||||
"opengist": "ghcr.io/thomiceli/opengist:1.15"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: ghcr.io/thomiceli/opengist:1.15"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"opengist ghcr.io/thomiceli/opengist:1.15 running=true restarts=0"
|
||||
]
|
||||
},
|
||||
"final_phase": "done",
|
||||
"hold_reason": null,
|
||||
"update_error": null
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:29][0;00m _____ __ __
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:29][0;00m | __ \ | \/ |
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:29][0;00m | |__) |___ _ __ ___ | \ / |
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:29][0;00m | _ // _ \| '_ ` _ \| |\/| |
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:29][0;00m | | \ \ (_) | | | | | | | | |
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:29][0;00m |_| \_\___/|_| |_| |_|_| |_|
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:29][0;00m
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:29][0;00m The beautiful, powerful, self-hosted Rom manager and player
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:29][0;00m
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:29][0;00m Version: 5.3.0
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:29][0;00m
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:29][0;00m No OpenTelemetry environment variables found, disabling OpenTelemetry SDK
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:29][0;00m REDIS_HOST is set, not starting internal valkey-server
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:29][0;00m Running database migrations
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:45][0;00m Database migrations succeeded
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[startup][36m[2026-09-21 21:29:56] [0mRunning startup tasks
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[startup][36m[2026-09-21 21:29:56] [0mCleared 2 job(s) left behind by the old scheduler
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[startup][36m[2026-09-21 21:29:56] [0mInitializing cache with fixtures data
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[startup][36m[2026-09-21 21:29:56] [0mStartup tasks completed
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:57][0;00m Starting backend
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:57][0;00m Starting RQ cron scheduler
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:57][0;00m Starting RQ worker
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:29:57][0;00m Starting RQ scan worker
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:30:00][0;00m Starting nginx
|
||||
romm | 2026/09/21 21:30:00 [notice] 131#131: js vm init njs: 0000710FFA2DDB00
|
||||
romm | [0;32mINFO: [0;34m[RomM][0;95m[init][0;36m[2026-09-21 21:30:00][0;00m 🚀 RomM is now available at http://0.0.0.0:8080
|
||||
romm | 21:30:00 Loading cron configuration from tasks.cron_config
|
||||
romm | 21:30:00 Worker ae1444165f644e9294bf27d3ebcefdb8: started with PID 104, version 2.12.0
|
||||
romm | 21:30:00 Worker ae1444165f644e9294bf27d3ebcefdb8: subscribing to channel rq:pubsub:ae1444165f644e9294bf27d3ebcefdb8
|
||||
romm | 21:30:00 *** Listening on high, default, low...
|
||||
romm | 21:30:00 Acquired scheduler lock for default
|
||||
romm | 21:30:00 Acquired scheduler lock for low
|
||||
romm | 21:30:00 Acquired scheduler lock for high
|
||||
romm | 21:30:00 Scheduler for default, low, high started with PID 146
|
||||
romm | 21:30:00 Worker e3223e339f794c82bec23fdda811216c: started with PID 107, version 2.12.0
|
||||
romm | 21:30:00 Worker e3223e339f794c82bec23fdda811216c: subscribing to channel rq:pubsub:e3223e339f794c82bec23fdda811216c
|
||||
romm | 21:30:00 *** Listening on scans...
|
||||
romm | 21:30:00 Acquired scheduler lock for scans
|
||||
romm | 21:30:00 Scheduler for scans started with PID 148
|
||||
romm | INFO: [RomM][nginx][2026-09-21 21:30:05] 127.0.0.1 | - | GET / 200 | 5878 | Unknown Unknown | 0.000
|
||||
romm | INFO: [RomM][nginx][2026-09-21 21:30:10] 172.18.0.7 | - | GET / 200 | 2206 | Unknown Unknown | 0.000
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[cron_config][36m[2026-09-21 21:30:17] [0mScheduled 'build_recommendations' at '30 5 * * *'
|
||||
romm-db | 2026-09-21 21:28:14+02:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:11.4.13+maria~ubu2404 started.
|
||||
romm-db | 2026-09-21 21:28:14+02:00 [Warn] [Entrypoint]: /sys/fs/cgroup///memory.pressure not writable, functionality unavailable to MariaDB
|
||||
romm-db | 2026-09-21 21:28:14+02:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
|
||||
romm-db | 2026-09-21 21:28:14+02:00 [Note] [Entrypoint]: Entrypoint script for MariaDB Server 1:11.4.13+maria~ubu2404 started.
|
||||
romm-db | 2026-09-21 21:28:14+02:00 [Note] [Entrypoint]: MariaDB upgrade not required
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[cron_config][36m[2026-09-21 21:30:17] [0mScheduled 'cleanup_zip_cache' at '0 4 * * *'
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[cron_config][36m[2026-09-21 21:30:17] [0mScheduled 'cleanup_netplay' at '*/30 * * * *'
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[cron_config][36m[2026-09-21 21:30:17] [0mScheduled 'cleanup_upload_tmp' at '0 * * * *'
|
||||
romm | 21:30:17 Registered 'tasks.tasks.run_task_by_name' to run on low with cron schedule '30 5 * * *'
|
||||
romm | 21:30:17 Registered 'tasks.tasks.run_task_by_name' to run on low with cron schedule '0 4 * * *'
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] Starting MariaDB 11.4.13-MariaDB-ubu2404 source revision 170b1d70737be6f134448f51713cdc1ae215b420 server_uid +Q9/tt9ag22l9EQsPkibJVRvRoU= as process 1
|
||||
romm | 21:30:17 Registered 'tasks.tasks.run_task_by_name' to run on low with cron schedule '*/30 * * * *'
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] InnoDB: Compressed tables use zlib 1.3
|
||||
romm | 21:30:17 Registered 'tasks.tasks.run_task_by_name' to run on low with cron schedule '0 * * * *'
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] InnoDB: Number of transaction pools: 1
|
||||
romm | 21:30:17 Successfully registered 4 cron jobs from 'tasks.cron_config'
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] InnoDB: Using crc32 + pclmulqdq instructions
|
||||
romm | 21:30:17 CronScheduler 090f2c2a50a2:101:0f34e0: starting...
|
||||
romm-db | 2026-09-21 21:28:14 0 [Warning] mariadbd: io_uring_queue_init() failed with EPERM: sysctl kernel.io_uring_disabled has the value 2, or 1 and the user of the process is not a member of sysctl kernel.io_uring_group. (see man 2 io_uring_setup).
|
||||
romm | 21:30:17 CronScheduler 090f2c2a50a2:101:0f34e0: registering birth...
|
||||
romm-db | create_uring failed: falling back to libaio
|
||||
romm | INFO: [RomM][nginx][2026-09-21 21:30:25] 172.18.0.8 | 192.168.0.180 | GET /api/heartbeat 499 | 0 | Unknown Unknown | 15.016
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] InnoDB: Using Linux native AIO
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[server][36m[2026-09-21 21:30:36] [0mStarted server process [119]
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] InnoDB: innodb_buffer_pool_size_max=8388608m, innodb_buffer_pool_size=128m
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[on][36m[2026-09-21 21:30:36] [0mWaiting for application startup.
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] InnoDB: Completed initialization of buffer pool
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[on][36m[2026-09-21 21:30:37] [0mApplication startup complete.
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] InnoDB: File system buffers for log disabled (block size=512 bytes)
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[server][36m[2026-09-21 21:30:37] [0mStarted server process [121]
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] InnoDB: End of log at LSN=3058473
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[on][36m[2026-09-21 21:30:37] [0mWaiting for application startup.
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] InnoDB: Opened 3 undo tablespaces
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] InnoDB: 128 rollback segments in 3 undo tablespaces are active.
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] InnoDB: Setting file './ibtmp1' size to 12.000MiB. Physically writing the file full; Please wait ...
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] InnoDB: File './ibtmp1' size is now 12.000MiB.
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] InnoDB: log sequence number 3058473; transaction id 2446
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] InnoDB: Loading buffer pool(s) from /var/lib/mysql/ib_buffer_pool
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[on][36m[2026-09-21 21:30:38] [0mApplication startup complete.
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] Plugin 'FEEDBACK' is disabled.
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[logs][36m[2026-09-21 21:30:38] [0mLog stream forwarder started
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[server][36m[2026-09-21 21:30:39] [0mStarted server process [118]
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[on][36m[2026-09-21 21:30:39] [0mWaiting for application startup.
|
||||
romm | [32mINFO[39m: [34m[RomM][95m[on][36m[2026-09-21 21:30:39] [0mApplication startup complete.
|
||||
romm | INFO: [RomM][nginx][2026-09-21 21:30:35] 127.0.0.1 | - | GET / 200 | 5878 | Unknown Unknown | 0.000
|
||||
romm | INFO: [RomM][nginx][2026-09-21 21:30:38] 172.18.0.8 | 192.168.0.180 | GET /api/heartbeat 200 | 543 | Unknown Unknown | 7.435
|
||||
romm | INFO: [RomM][nginx][2026-09-21 21:30:38] 172.18.0.8 | 192.168.0.180 | GET /api/heartbeat 200 | 543 | Unknown Unknown | 0.013
|
||||
romm | INFO: [RomM][nginx][2026-09-21 21:30:39] 172.18.0.8 | 192.168.0.180 | POST /api/login 401 | 43 | Unknown Unknown | 0.587
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] Plugin 'wsrep-provider' is disabled.
|
||||
romm-db | 2026-09-21 21:28:14 0 [Note] InnoDB: Buffer pool(s) load completed at 260921 21:28:14
|
||||
romm-db | 2026-09-21 21:28:16 0 [Note] Server socket created on IP: '0.0.0.0', port: '3306'.
|
||||
romm-db | 2026-09-21 21:28:16 0 [Note] Server socket created on IP: '::', port: '3306'.
|
||||
romm-db | 2026-09-21 21:28:16 0 [Note] mariadbd: Event Scheduler: Loaded 0 events
|
||||
romm-db | 2026-09-21 21:28:16 0 [Note] mariadbd: ready for connections.
|
||||
romm-db | Version: '11.4.13-MariaDB-ubu2404' socket: '/run/mysqld/mysqld.sock' port: 3306 mariadb.org binary distribution
|
||||
romm-db | 2026-09-21 21:29:45 22 [Warning] Aborted connection 22 to db: 'romm' user: 'romm' host: '172.20.0.4' (Got an error reading communication packets)
|
||||
romm-redis | 1:C 21 Sep 2026 21:28:14.156 # WARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. Being disabled, it can also cause failures without low memory condition, see https://github.com/jemalloc/jemalloc/issues/1328. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
|
||||
romm-redis | 1:C 21 Sep 2026 21:28:14.156 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
|
||||
romm-redis | 1:C 21 Sep 2026 21:28:14.156 * Redis version=7.4.11, bits=64, commit=00000000, modified=0, pid=1, just started
|
||||
romm-redis | 1:C 21 Sep 2026 21:28:14.156 * Configuration loaded
|
||||
romm-redis | 1:M 21 Sep 2026 21:28:14.157 * Increased maximum number of open files to 10032 (it was originally set to 1024).
|
||||
romm-redis | 1:M 21 Sep 2026 21:28:14.157 * monotonic clock: POSIX clock_gettime
|
||||
romm-redis | 1:M 21 Sep 2026 21:28:14.158 * Running mode=standalone, port=6379.
|
||||
romm-redis | 1:M 21 Sep 2026 21:28:14.159 * Server initialized
|
||||
romm-redis | 1:M 21 Sep 2026 21:28:14.159 * Reading RDB base file on AOF loading...
|
||||
romm-redis | 1:M 21 Sep 2026 21:28:14.159 * Loading RDB produced by version 7.4.11
|
||||
romm-redis | 1:M 21 Sep 2026 21:28:14.159 * RDB age 68 seconds
|
||||
romm-redis | 1:M 21 Sep 2026 21:28:14.159 * RDB memory usage when created 0.90 Mb
|
||||
romm-redis | 1:M 21 Sep 2026 21:28:14.159 * RDB is base AOF
|
||||
romm-redis | 1:M 21 Sep 2026 21:28:14.159 * Done loading RDB, keys loaded: 0, keys expired: 0.
|
||||
romm-redis | 1:M 21 Sep 2026 21:28:14.159 * DB loaded from base file appendonly.aof.1.base.rdb: 0.000 seconds
|
||||
romm-redis | 1:M 21 Sep 2026 21:28:14.223 * DB loaded from incr file appendonly.aof.1.incr.aof: 0.064 seconds
|
||||
romm-redis | 1:M 21 Sep 2026 21:28:14.223 * DB loaded from append only file: 0.064 seconds
|
||||
romm-redis | 1:M 21 Sep 2026 21:28:14.223 * Opening AOF incr file appendonly.aof.1.incr.aof on server start
|
||||
romm-redis | 1:M 21 Sep 2026 21:28:14.223 * Ready to accept connections tcp
|
||||
romm-redis | 1:M 21 Sep 2026 21:29:15.065 * 10000 changes in 60 seconds. Saving...
|
||||
romm-redis | 1:M 21 Sep 2026 21:29:15.066 * Background saving started by pid 52
|
||||
romm-redis | 52:C 21 Sep 2026 21:29:15.192 * DB saved on disk
|
||||
romm-redis | 52:C 21 Sep 2026 21:29:15.193 * Fork CoW for RDB: current 1 MB, peak 1 MB, average 0 MB
|
||||
romm-redis | 1:M 21 Sep 2026 21:29:15.268 * Background saving terminated with success
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"before": {
|
||||
"hu": [
|
||||
{
|
||||
"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.",
|
||||
"text": "Naprakész"
|
||||
}
|
||||
],
|
||||
"en": [
|
||||
{
|
||||
"title": "This app is running the newest version available.",
|
||||
"text": "Up to date"
|
||||
}
|
||||
]
|
||||
},
|
||||
"after": {
|
||||
"hu": [
|
||||
{
|
||||
"title": "Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.",
|
||||
"text": "Frissítés elérhető — ma"
|
||||
}
|
||||
],
|
||||
"en": [
|
||||
{
|
||||
"title": "A newer version of this app is available. Select the Update button to start it.",
|
||||
"text": "Update available — today"
|
||||
}
|
||||
]
|
||||
},
|
||||
"drill_commit": "f72102b7217b"
|
||||
}
|
||||
@@ -1,11 +1,27 @@
|
||||
21:09:03 ==== romm: rommapp/romm:5.0.0 -> rommapp/romm:5.3.0 (sub=arcade, class=db-mariadb)
|
||||
21:09:06 [1] made the drive paths this app requires: ['/mnt/felhom-drives/scratch_hdd/userdata/romm']
|
||||
21:09:06 [1] required fields filled beyond DOMAIN/SUBDOMAIN: ['HDD_PATH']
|
||||
21:09:06 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
|
||||
21:09:26 [1] deployed, controller state=degraded, pinned=None
|
||||
21:09:26 [1] NOTE: the controller's own state is 'degraded', not 'running' — recorded, not treated as a failure; the fixture's front-door wait is the real gate
|
||||
21:09:28 [2] seeding through the app's own front door
|
||||
21:10:14 romm: POST /api/users http=403 :: CSRF token verification failed
|
||||
21:10:14 romm: POST /api/users http=403 :: CSRF token verification failed
|
||||
21:10:14 romm: no unauthenticated route created the first account
|
||||
21:10:14 [9] verdict inconclusive -> /mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/update-night-2026-09-21/apps/romm/verdict.json
|
||||
21:27:03 ==== romm: rommapp/romm:5.0.0 -> rommapp/romm:5.3.0 (sub=arcade, class=db-mariadb)
|
||||
21:27:05 [1] made the drive paths this app requires: ['/mnt/felhom-drives/scratch_hdd/userdata/romm']
|
||||
21:27:05 [1] required fields filled beyond DOMAIN/SUBDOMAIN: ['HDD_PATH']
|
||||
21:27:05 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
|
||||
21:27:56 [1] deployed, controller state=running, pinned={'romm': 'rommapp/romm:5.0.0', 'romm-db': 'mariadb:11.4', 'romm-redis': 'redis:7-alpine'}
|
||||
21:27:58 [2] seeding through the app's own front door
|
||||
21:28:02 romm: POST /api/users http=201
|
||||
21:28:02 [3] control C1 — reading the seed back BEFORE the update
|
||||
21:28:04 romm: login as the seeded user http=200 ok=True
|
||||
21:28:04 [4] „Mentés most" -> 200 {'ok': True, 'message': 'Mentés elindítva'}
|
||||
21:29:04 [4] backup idle; last=None
|
||||
21:29:05 [5] drill commit f72102b7217b: romm rommapp/romm:5.0.0 -> rommapp/romm:5.3.0 (push rc=0)
|
||||
21:29:10 [5] badge HU: [{'title': 'Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.', 'text': 'Frissítés elérhető — ma'}]
|
||||
21:29:10 [5] badge EN: [{'title': 'A newer version of this app is available. Select the Update button to start it.', 'text': 'Update available — today'}]
|
||||
21:29:10 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}
|
||||
21:29:10 + 0.0s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None
|
||||
21:29:11 + 1.1s phase=pulling label=Új verzió letöltése… err=None hold=None
|
||||
21:29:23 + 13.4s phase=starting label=Indítás az új verzióval… err=None hold=None
|
||||
21:29:29 + 19.5s phase=verifying label=Működés ellenőrzése… err=None hold=None
|
||||
21:30:10 + 60.5s phase=done label=Frissítve err=None hold=None
|
||||
21:30:10 [7] reading the seed back AFTER the update
|
||||
21:30:40 romm: login as the seeded user http=200 ok=True
|
||||
21:30:43 [8] pinned = {'romm': 'rommapp/romm:5.3.0', 'romm-db': 'mariadb:11.4', 'romm-redis': 'redis:7-alpine'}
|
||||
21:30:43 [8] installed = {'romm': 'rommapp/romm:5.3.0', 'romm-db': 'mariadb:11.4', 'romm-redis': 'redis:7-alpine'}
|
||||
21:30:43 [8] compose = ['image: rommapp/romm:5.3.0', 'image: mariadb:11.4', 'image: redis:7-alpine']
|
||||
21:30:43 [8] inspect = ['romm rommapp/romm:5.3.0 running=true restarts=0', 'romm-db mariadb:11.4 running=true restarts=0', 'romm-redis redis:7-alpine running=true restarts=0']
|
||||
21:30:43 [9] verdict proven -> /mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/update-night-2026-09-21/apps/romm/verdict.json
|
||||
|
||||
@@ -1,14 +1,26 @@
|
||||
{
|
||||
"pinned_images": null,
|
||||
"installed_images": {},
|
||||
"catalog_images": null,
|
||||
"pinned_images": {
|
||||
"romm": "rommapp/romm:5.0.0",
|
||||
"romm-db": "mariadb:11.4",
|
||||
"romm-redis": "redis:7-alpine"
|
||||
},
|
||||
"installed_images": {
|
||||
"romm": "rommapp/romm:5.0.0",
|
||||
"romm-db": "mariadb:11.4",
|
||||
"romm-redis": "redis:7-alpine"
|
||||
},
|
||||
"catalog_images": {
|
||||
"romm": "rommapp/romm:5.0.0",
|
||||
"romm-db": "mariadb:11.4",
|
||||
"romm-redis": "redis:7-alpine"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: rommapp/romm:5.0.0",
|
||||
"image: mariadb:11.4",
|
||||
"image: redis:7-alpine"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"romm rommapp/romm:5.0.0 running=false restarts=0",
|
||||
"romm rommapp/romm:5.0.0 running=true restarts=0",
|
||||
"romm-db mariadb:11.4 running=true restarts=0",
|
||||
"romm-redis redis:7-alpine running=true restarts=0"
|
||||
]
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
{
|
||||
"before": {
|
||||
"pinned_images": {
|
||||
"romm": "rommapp/romm:5.0.0",
|
||||
"romm-db": "mariadb:11.4",
|
||||
"romm-redis": "redis:7-alpine"
|
||||
},
|
||||
"installed_images": {
|
||||
"romm": "rommapp/romm:5.0.0",
|
||||
"romm-db": "mariadb:11.4",
|
||||
"romm-redis": "redis:7-alpine"
|
||||
},
|
||||
"catalog_images": {
|
||||
"romm": "rommapp/romm:5.0.0",
|
||||
"romm-db": "mariadb:11.4",
|
||||
"romm-redis": "redis:7-alpine"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: rommapp/romm:5.0.0",
|
||||
"image: mariadb:11.4",
|
||||
"image: redis:7-alpine"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"romm rommapp/romm:5.0.0 running=true restarts=0",
|
||||
"romm-db mariadb:11.4 running=true restarts=0",
|
||||
"romm-redis redis:7-alpine running=true restarts=0"
|
||||
]
|
||||
},
|
||||
"after": {
|
||||
"pinned_images": {
|
||||
"romm": "rommapp/romm:5.3.0",
|
||||
"romm-db": "mariadb:11.4",
|
||||
"romm-redis": "redis:7-alpine"
|
||||
},
|
||||
"installed_images": {
|
||||
"romm": "rommapp/romm:5.3.0",
|
||||
"romm-db": "mariadb:11.4",
|
||||
"romm-redis": "redis:7-alpine"
|
||||
},
|
||||
"catalog_images": {
|
||||
"romm": "rommapp/romm:5.3.0",
|
||||
"romm-db": "mariadb:11.4",
|
||||
"romm-redis": "redis:7-alpine"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: rommapp/romm:5.3.0",
|
||||
"image: mariadb:11.4",
|
||||
"image: redis:7-alpine"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"romm rommapp/romm:5.3.0 running=true restarts=0",
|
||||
"romm-db mariadb:11.4 running=true restarts=0",
|
||||
"romm-redis redis:7-alpine running=true restarts=0"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"accepted": true,
|
||||
"http": "202",
|
||||
"phases": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"phase": "safety-dump",
|
||||
"label": "Adatbázis pillanatkép…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 1.1,
|
||||
"phase": "pulling",
|
||||
"label": "Új verzió letöltése…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 13.4,
|
||||
"phase": "starting",
|
||||
"label": "Indítás az új verzióval…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 19.5,
|
||||
"phase": "verifying",
|
||||
"label": "Működés ellenőrzése…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 60.5,
|
||||
"phase": "done",
|
||||
"label": "Frissítve",
|
||||
"updating": false,
|
||||
"error": null,
|
||||
"hold": null
|
||||
}
|
||||
],
|
||||
"duration_s": 60.6,
|
||||
"final_phase": "done",
|
||||
"update_error": null,
|
||||
"hold_reason": null,
|
||||
"state": "running"
|
||||
}
|
||||
@@ -3,20 +3,56 @@
|
||||
"app": "romm",
|
||||
"venue": "guest 9202 demo-hp-scratch, controller 0.261.0",
|
||||
"class": "db-mariadb",
|
||||
"from": {},
|
||||
"to": {},
|
||||
"verdict": "inconclusive",
|
||||
"seed_read_before": false,
|
||||
"seed_read_after": false,
|
||||
"healthy_after": false,
|
||||
"migration_observed": null,
|
||||
"from": {
|
||||
"romm": "rommapp/romm:5.0.0",
|
||||
"romm-db": "mariadb:11.4",
|
||||
"romm-redis": "redis:7-alpine"
|
||||
},
|
||||
"to": {
|
||||
"romm": "rommapp/romm:5.3.0",
|
||||
"romm-db": "mariadb:11.4",
|
||||
"romm-redis": "redis:7-alpine"
|
||||
},
|
||||
"verdict": "proven",
|
||||
"seed_read_before": true,
|
||||
"seed_read_after": true,
|
||||
"healthy_after": true,
|
||||
"migration_observed": "romm | \u001b[0;32mINFO: \u001b[0;34m[RomM]\u001b[0;95m[init]\u001b[0;36m[2026-09-21 21:29:29]\u001b[0;00m Running database migrations",
|
||||
"abort": "not-attempted",
|
||||
"abort_detail": null,
|
||||
"duration_s": 70.7,
|
||||
"measured_at": "2026-09-21T19:09:04.045090+00:00",
|
||||
"duration_s": 60.6,
|
||||
"measured_at": "2026-09-21T19:27:03.627376+00:00",
|
||||
"evidence": "apps/romm/",
|
||||
"notes": [
|
||||
"seed refused through the app's own route — see log.txt for what was tried",
|
||||
"seed route did not work tonight"
|
||||
]
|
||||
"notes": [],
|
||||
"badge_catchup_seconds": 4.4,
|
||||
"observables_after": {
|
||||
"pinned_images": {
|
||||
"romm": "rommapp/romm:5.3.0",
|
||||
"romm-db": "mariadb:11.4",
|
||||
"romm-redis": "redis:7-alpine"
|
||||
},
|
||||
"installed_images": {
|
||||
"romm": "rommapp/romm:5.3.0",
|
||||
"romm-db": "mariadb:11.4",
|
||||
"romm-redis": "redis:7-alpine"
|
||||
},
|
||||
"catalog_images": {
|
||||
"romm": "rommapp/romm:5.3.0",
|
||||
"romm-db": "mariadb:11.4",
|
||||
"romm-redis": "redis:7-alpine"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: rommapp/romm:5.3.0",
|
||||
"image: mariadb:11.4",
|
||||
"image: redis:7-alpine"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"romm rommapp/romm:5.3.0 running=true restarts=0",
|
||||
"romm-db mariadb:11.4 running=true restarts=0",
|
||||
"romm-redis redis:7-alpine running=true restarts=0"
|
||||
]
|
||||
},
|
||||
"final_phase": "done",
|
||||
"hold_reason": null,
|
||||
"update_error": null
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
21:36:12 ==== FAILWALK tandoor: the household's way out of a held update
|
||||
21:36:15 [1] held state: phase=failed hold='A(z) tandoor frissítése 2026-09-21 21:22-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:15 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.'
|
||||
21:36:15 update_error = 'A(z) tandoor frissítése 2026-09-21 21:22-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:15 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.'
|
||||
21:36:15 pinned = {'tandoor': 'ghcr.io/tandoorrecipes/recipes:2.6.15', 'tandoor-postgres': 'postgres:16-alpine'}
|
||||
21:36:15 installed = {'tandoor': 'ghcr.io/tandoorrecipes/recipes:2.6.13', 'tandoor-postgres': 'postgres:16-alpine'}
|
||||
21:36:15 compose = ['image: ghcr.io/tandoorrecipes/recipes:2.6.15', 'image: postgres:16-alpine']
|
||||
21:36:15 inspect = []
|
||||
21:36:15 [2] hold sentence HU: ['Tandoor Recipes — Felhom.eu Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Telepített alkalmazás nem fut: Tandoor Recipes (stopped) Rendszermonitor → Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások Tandoor Recipes Leállítva Frissítés elérhető — ma Megnyitás ↗ Napló Exportálás Beállítások A(z) tandoor frissítése 2026-09-21 21:22-kor nem sikerült, és az alkalmazás nem indult el az új verzióval.', 'Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:15 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.', 'Mentések Receptkezelő és étkezés tervező a családnak ~512M RAM home Pi kompatibilis Áthelyezés másik tárhelyre Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át.', 'Receptek gyűjtése és rendszerezése egy helyen Receptek importálása weboldalakról egy kattintással Heti étkezés tervezés és bevásárlólista generálás Több felhasználó - a család együtt tervezhet Receptek megosztása linkkel családtagokkal, barátokkal Első lépések Nyisd meg a recipes.DOMAIN címet a böngészőben Hozd létre az admin fiókot Importáld az első receptet egy weboldalról (Bookmarklet) Próbáld ki az étkezés tervezőt Hívd meg a családtagokat Dokumentáció Hivatalos dokumentáció ↗']
|
||||
21:36:15 [2] hold sentence EN: ['Tandoor Recipes — Felhom.eu Launcher Dashboard Apps Storage Drives Network storage Backup Overview Remote backup Apps Restore Sharing Network sharing System monitor Debug Settings System Notifications Security and access 0.261.0 Magyar English Sign out ↗ An installed app is not running: Tandoor Recipes (stopped) System monitor → The hub connection is off — central monitoring is not running System monitor → ← Apps Tandoor Recipes Stopped Update available — today Open ↗ Log Export Settings A(z) tandoor frissítése 2026-09-21 21:22-kor nem sikerült, és az alkalmazás nem indult el az új verzióval.', 'Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:15 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.', 'Backups A recipe manager and meal planner for the household ~512M RAM home Runs on Pi Move to another storage You can move this app’s data to another connected storage.', 'Keep all your recipes in one place Import a recipe from a web page with one click Plan the week's meals and get a shopping list out of it Several people - the household can plan together Share a recipe by link with family and friends First steps Open recipes.DOMAIN in your browser Create the admin account Import your first recipe from a web page (Bookmarklet) Try the meal planner Invite the household Documentation Official documentation ↗']
|
||||
21:36:15 [3] the last migration line the app printed, verbatim: None
|
||||
21:36:15 [R] restoring tandoor from snapshot 'helyi' (of 1 offered)
|
||||
21:36:15 [R] POST /backup/restore -> HTTP/2 302 ['location: /backups/restore?flash=flash.restore.started']
|
||||
21:36:15 + 0.0s restore (True, None, None)
|
||||
21:36:47 + 32.4s restore (False, None, None)
|
||||
21:36:47 [R] after restore: state=running hold=None phase=None
|
||||
21:36:50 [4] restore: HTTP/2 302 in 32.4s; state=running hold=None
|
||||
21:36:50 pinned after = {'tandoor': 'ghcr.io/tandoorrecipes/recipes:2.6.13', 'tandoor-postgres': 'postgres:16-alpine'}
|
||||
21:36:50 inspect after = ['tandoor ghcr.io/tandoorrecipes/recipes:2.6.13 running=true restarts=0', 'tandoor-postgres postgres:16-alpine running=true restarts=0']
|
||||
21:36:59 tandoor: createsuperuser :: Running django-vite in production mode (no HMR) Superuser created successfully.
|
||||
21:37:03 tandoor: seeded superuser drill74a269
|
||||
21:37:09 tandoor: readback of the seeded account found=True
|
||||
21:37:09 [5] the app answers its own front door again and holds data: True
|
||||
21:37:10 [6] sentence after the restore HU: ['Tandoor Recipes — Felhom.eu Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások Tandoor Recipes Nem egészséges Frissítés elérhető — ma Megnyitás ↗ Napló Exportálás Beállítások Receptkezelő és étkezés tervező a családnak ~512M RAM home Pi kompatibilis Áthelyezés másik tárhelyre Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át.', 'Receptek gyűjtése és rendszerezése egy helyen Receptek importálása weboldalakról egy kattintással Heti étkezés tervezés és bevásárlólista generálás Több felhasználó - a család együtt tervezhet Receptek megosztása linkkel családtagokkal, barátokkal Első lépések Nyisd meg a recipes.DOMAIN címet a böngészőben Hozd létre az admin fiókot Importáld az első receptet egy weboldalról (Bookmarklet) Próbáld ki az étkezés tervezőt Hívd meg a családtagokat Dokumentáció Hivatalos dokumentáció ↗']
|
||||
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"leg": "failwalk",
|
||||
"app": "tandoor",
|
||||
"held": {
|
||||
"state": "stopped",
|
||||
"updating": false,
|
||||
"update_phase": "failed",
|
||||
"update_phase_label": "A frissítés nem sikerült",
|
||||
"update_error": "A(z) tandoor frissítése 2026-09-21 21:22-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:15 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"hold_reason": "A(z) tandoor frissítése 2026-09-21 21:22-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:15 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"observables": {
|
||||
"pinned_images": {
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.15",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"installed_images": {
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.13",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"catalog_images": {
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.15",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: ghcr.io/tandoorrecipes/recipes:2.6.15",
|
||||
"image: postgres:16-alpine"
|
||||
],
|
||||
"docker_inspect": []
|
||||
}
|
||||
},
|
||||
"hold_sentences": {
|
||||
"hu": [
|
||||
"Tandoor Recipes — Felhom.eu Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Telepített alkalmazás nem fut: Tandoor Recipes (stopped) Rendszermonitor → Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások Tandoor Recipes Leállítva Frissítés elérhető — ma Megnyitás ↗ Napló Exportálás Beállítások A(z) tandoor frissítése 2026-09-21 21:22-kor nem sikerült, és az alkalmazás nem indult el az új verzióval.",
|
||||
"Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:15 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"Mentések Receptkezelő és étkezés tervező a családnak ~512M RAM home Pi kompatibilis Áthelyezés másik tárhelyre Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át.",
|
||||
"Receptek gyűjtése és rendszerezése egy helyen Receptek importálása weboldalakról egy kattintással Heti étkezés tervezés és bevásárlólista generálás Több felhasználó - a család együtt tervezhet Receptek megosztása linkkel családtagokkal, barátokkal Első lépések Nyisd meg a recipes.DOMAIN címet a böngészőben Hozd létre az admin fiókot Importáld az első receptet egy weboldalról (Bookmarklet) Próbáld ki az étkezés tervezőt Hívd meg a családtagokat Dokumentáció Hivatalos dokumentáció ↗"
|
||||
],
|
||||
"en": [
|
||||
"Tandoor Recipes — Felhom.eu Launcher Dashboard Apps Storage Drives Network storage Backup Overview Remote backup Apps Restore Sharing Network sharing System monitor Debug Settings System Notifications Security and access 0.261.0 Magyar English Sign out ↗ An installed app is not running: Tandoor Recipes (stopped) System monitor → The hub connection is off — central monitoring is not running System monitor → ← Apps Tandoor Recipes Stopped Update available — today Open ↗ Log Export Settings A(z) tandoor frissítése 2026-09-21 21:22-kor nem sikerült, és az alkalmazás nem indult el az új verzióval.",
|
||||
"Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:15 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"Backups A recipe manager and meal planner for the household ~512M RAM home Runs on Pi Move to another storage You can move this app’s data to another connected storage.",
|
||||
"Keep all your recipes in one place Import a recipe from a web page with one click Plan the week's meals and get a shopping list out of it Several people - the household can plan together Share a recipe by link with family and friends First steps Open recipes.DOMAIN in your browser Create the admin account Import your first recipe from a web page (Bookmarklet) Try the meal planner Invite the household Documentation Official documentation ↗"
|
||||
]
|
||||
},
|
||||
"last_migration_line": null,
|
||||
"way_out": {
|
||||
"ok": true,
|
||||
"snapshot_id": "helyi",
|
||||
"snapshots": [
|
||||
{
|
||||
"time": "2026-09-21T19:16:48Z",
|
||||
"short_id": "helyi",
|
||||
"tier": 1,
|
||||
"drive_label": "Belső SSD (rendszer)"
|
||||
}
|
||||
],
|
||||
"http": "HTTP/2 302",
|
||||
"location": [
|
||||
"location: /backups/restore?flash=flash.restore.started"
|
||||
],
|
||||
"seconds": 32.4,
|
||||
"state_after": "running",
|
||||
"hold_after": null,
|
||||
"observables_after": {
|
||||
"pinned_images": {
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.13",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"installed_images": {
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.13",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"catalog_images": {
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.15",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: ghcr.io/tandoorrecipes/recipes:2.6.13",
|
||||
"image: postgres:16-alpine"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"tandoor ghcr.io/tandoorrecipes/recipes:2.6.13 running=true restarts=0",
|
||||
"tandoor-postgres postgres:16-alpine running=true restarts=0"
|
||||
]
|
||||
}
|
||||
},
|
||||
"data_after_restore": {
|
||||
"route": "re-seeded after the restore",
|
||||
"ok": true,
|
||||
"note": "the pre-update seed token did not survive the failed run's process, so this re-seeds AFTER the restore: it proves the app WORKS again on the old version, not that the specific pre-update row survived. Stated rather than glossed."
|
||||
},
|
||||
"sentences_after_restore": {
|
||||
"hu": [
|
||||
"Tandoor Recipes — Felhom.eu Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások Tandoor Recipes Nem egészséges Frissítés elérhető — ma Megnyitás ↗ Napló Exportálás Beállítások Receptkezelő és étkezés tervező a családnak ~512M RAM home Pi kompatibilis Áthelyezés másik tárhelyre Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át.",
|
||||
"Receptek gyűjtése és rendszerezése egy helyen Receptek importálása weboldalakról egy kattintással Heti étkezés tervezés és bevásárlólista generálás Több felhasználó - a család együtt tervezhet Receptek megosztása linkkel családtagokkal, barátokkal Első lépések Nyisd meg a recipes.DOMAIN címet a böngészőben Hozd létre az admin fiókot Importáld az első receptet egy weboldalról (Bookmarklet) Próbáld ki az étkezés tervezőt Hívd meg a családtagokat Dokumentáció Hivatalos dokumentáció ↗"
|
||||
],
|
||||
"en": [
|
||||
"Tandoor Recipes — Felhom.eu Launcher Dashboard Apps Storage Drives Network storage Backup Overview Remote backup Apps Restore Sharing Network sharing System monitor Debug Settings System Notifications Security and access 0.261.0 Magyar English Sign out ↗ The hub connection is off — central monitoring is not running System monitor → ← Apps Tandoor Recipes Not healthy Update available — today Open ↗ Log Export Settings A recipe manager and meal planner for the household ~512M RAM home Runs on Pi Move to another storage You can move this app’s data to another connected storage.",
|
||||
"Keep all your recipes in one place Import a recipe from a web page with one click Plan the week's meals and get a shopping list out of it Several people - the household can plan together Share a recipe by link with family and friends First steps Open recipes.DOMAIN in your browser Create the admin account Import your first recipe from a web page (Bookmarklet) Try the meal planner Invite the household Documentation Official documentation ↗"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,654 @@
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="no-js">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Tandoor Recipes — Felhom.eu</title>
|
||||
|
||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg?v=0.261.0">
|
||||
<link rel="stylesheet" href="/static/style.css?v=0.261.0">
|
||||
<meta name="csrf-token" content="b0f2927caee41c5a97de77202486f9d0a2d3bb97124581393b77751acb8f4307">
|
||||
<script>
|
||||
|
||||
|
||||
document.documentElement.className=document.documentElement.className.replace('no-js','js');
|
||||
function csrfHeaders(){var el=document.querySelector('meta[name="csrf-token"]');return el?{'X-CSRF-Token':el.content}:{};}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function felhomConfirm(el,question,onYes){
|
||||
if(!el||el.dataset.fcOpen)return;
|
||||
el.dataset.fcOpen='1';
|
||||
var wrap=document.createElement('span');wrap.className='inline-confirm';
|
||||
var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question;
|
||||
var yes=document.createElement('button');yes.type='button';yes.className='btn btn-xs btn-danger';yes.textContent='Yes';
|
||||
var no=document.createElement('button');no.type='button';no.className='btn btn-xs btn-outline';no.textContent='Cancel';
|
||||
wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no);
|
||||
el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling);
|
||||
function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;}
|
||||
no.addEventListener('click',close);
|
||||
yes.addEventListener('click',function(){close();onYes();});
|
||||
}
|
||||
document.addEventListener('click',function(e){
|
||||
var btn=e.target.closest?e.target.closest('[data-confirm]'):null;
|
||||
if(!btn)return;
|
||||
e.preventDefault();
|
||||
felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){
|
||||
var form=btn.closest('form');
|
||||
if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
document.addEventListener('click',function(e){
|
||||
var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null;
|
||||
if(!btn)return;
|
||||
var group=btn.closest('.nav-group');
|
||||
if(!group)return;
|
||||
var willOpen=!group.classList.contains('is-open');
|
||||
document.querySelectorAll('.nav-group.is-open').forEach(function(g){
|
||||
g.classList.remove('is-open');
|
||||
var t=g.querySelector('.nav-group-toggle');
|
||||
if(t)t.setAttribute('aria-expanded','false');
|
||||
});
|
||||
if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');}
|
||||
});
|
||||
function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML='<h3>Message</h3><pre style="white-space:pre-wrap;word-break:break-word;background:var(--bg-2);padding:.75rem;border-radius:.375rem;font-size:.85rem;max-height:60vh;overflow-y:auto;user-select:text;cursor:text">'+msg.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')+'</pre><div class="modal-actions"><button class="btn btn-primary" onclick="document.getElementById(\'alert-modal\').remove()">OK</button></div>';o.appendChild(c);document.body.appendChild(o);}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display:none" aria-hidden="true">
|
||||
<symbol id="i-hard-drive" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 16h.01" /> <path d="M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" /> <path d="M21.946 12.013H2.054" /> <path d="M6 16h.01" /></symbol>
|
||||
<symbol id="i-cpu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20v2" /> <path d="M12 2v2" /> <path d="M17 20v2" /> <path d="M17 2v2" /> <path d="M2 12h2" /> <path d="M2 17h2" /> <path d="M2 7h2" /> <path d="M20 12h2" /> <path d="M20 17h2" /> <path d="M20 7h2" /> <path d="M7 20v2" /> <path d="M7 2v2" /> <rect x="4" y="4" width="16" height="16" rx="2" /> <rect x="8" y="8" width="8" height="8" rx="1" /></symbol>
|
||||
<symbol id="i-memory-stick" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 12v-2" /> <path d="M12 18v-2" /> <path d="M16 12v-2" /> <path d="M16 18v-2" /> <path d="M2 11h1.5" /> <path d="M20 18v-2" /> <path d="M20.5 11H22" /> <path d="M4 18v-2" /> <path d="M8 12v-2" /> <path d="M8 18v-2" /> <rect x="2" y="6" width="20" height="10" rx="2" /></symbol>
|
||||
<symbol id="i-thermometer" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 4v10.54a4 4 0 1 1-4 0V4a2 2 0 0 1 4 0Z" /></symbol>
|
||||
<symbol id="i-triangle-alert" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" /> <path d="M12 9v4" /> <path d="M12 17h.01" /></symbol>
|
||||
<symbol id="i-lock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="11" x="3" y="11" rx="2" ry="2" /> <path d="M7 11V7a5 5 0 0 1 10 0v4" /></symbol>
|
||||
<symbol id="i-cloud" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z" /></symbol>
|
||||
<symbol id="i-square" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" /></symbol>
|
||||
<symbol id="i-rotate-cw" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" /> <path d="M21 3v5h-5" /></symbol>
|
||||
<symbol id="i-file-text" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /> <path d="M14 2v5a1 1 0 0 0 1 1h5" /> <path d="M10 9H8" /> <path d="M16 13H8" /> <path d="M16 17H8" /></symbol>
|
||||
<symbol id="i-external-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6" /> <path d="M10 14 21 3" /> <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></symbol>
|
||||
<symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /></symbol>
|
||||
<symbol id="i-server" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="8" x="2" y="2" rx="2" ry="2" /> <rect width="20" height="8" x="2" y="14" rx="2" ry="2" /> <line x1="6" x2="6.01" y1="6" y2="6" /> <line x1="6" x2="6.01" y1="18" y2="18" /></symbol>
|
||||
<symbol id="i-share" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3" /> <circle cx="6" cy="12" r="3" /> <circle cx="18" cy="19" r="3" /> <line x1="8.59" x2="15.42" y1="13.51" y2="17.49" /> <line x1="15.41" x2="8.59" y1="6.51" y2="10.49" /></symbol>
|
||||
<symbol id="i-layout-grid" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="7" x="3" y="3" rx="1" /> <rect width="7" height="7" x="14" y="3" rx="1" /> <rect width="7" height="7" x="14" y="14" rx="1" /> <rect width="7" height="7" x="3" y="14" rx="1" /></symbol>
|
||||
<symbol id="i-rocket" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91 0z" /> <path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z" /> <path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0" /> <path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5" /></symbol>
|
||||
<symbol id="i-settings" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /> <circle cx="12" cy="12" r="3" /></symbol>
|
||||
<symbol id="i-bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.268 21a2 2 0 0 0 3.464 0" /> <path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" /></symbol>
|
||||
<symbol id="i-x" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18" /> <path d="m6 6 12 12" /></symbol>
|
||||
<symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5" /></symbol>
|
||||
<symbol id="i-pencil" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" /> <path d="m15 5 4 4" /></symbol>
|
||||
<symbol id="i-info" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /> <path d="M12 16v-4" /> <path d="M12 8h.01" /></symbol>
|
||||
<symbol id="i-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 15V3" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /> <path d="m7 10 5 5 5-5" /></symbol>
|
||||
<symbol id="i-upload" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v12" /> <path d="m17 8-5-5-5 5" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /></symbol>
|
||||
<symbol id="i-trash-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 11v6" /> <path d="M14 11v6" /> <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /> <path d="M3 6h18" /> <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></symbol>
|
||||
<symbol id="i-wrench" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z" /></symbol>
|
||||
<symbol id="i-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" /> <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" /></symbol>
|
||||
<symbol id="i-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34" /> <circle cx="11" cy="11" r="8" /></symbol>
|
||||
<symbol id="i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14" /> <path d="M12 5v14" /></symbol>
|
||||
<symbol id="i-chevron-down" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6" /></symbol>
|
||||
<symbol id="i-play" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z" /></symbol>
|
||||
<symbol id="i-pause" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="14" y="3" width="5" height="18" rx="1" /> <rect x="5" y="3" width="5" height="18" rx="1" /></symbol>
|
||||
<symbol id="i-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h16" /> <path d="M4 6h16" /> <path d="M4 18h16" /></symbol>
|
||||
</svg>
|
||||
<header class="mobile-topbar">
|
||||
<a href="/" class="mobile-topbar-logo"><img src="/static/felhom-logo.svg?v=0.261.0" alt="Felhom.eu"></a>
|
||||
<button type="button" class="nav-burger" aria-expanded="false" aria-controls="sidebar" aria-label="Menu">
|
||||
<svg class="ico"><use href="#i-menu"/></svg>
|
||||
</button>
|
||||
</header>
|
||||
<div class="nav-backdrop" hidden></div>
|
||||
<nav class="sidebar" id="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<img src="/static/felhom-logo.svg?v=0.261.0" alt="Felhom.eu" class="sidebar-logo">
|
||||
</div>
|
||||
<ul class="nav-links">
|
||||
<li><a href="/launcher" class=""><svg class="ico"><use href="#i-rocket"/></svg>Launcher</a></li>
|
||||
<li><a href="/dashboard" class=""><svg class="ico"><use href="#i-layout-grid"/></svg>Dashboard</a></li>
|
||||
<li><a href="/stacks" class="active"><svg class="ico"><use href="#i-cloud"/></svg>Apps</a></li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-storage"><svg class="ico"><use href="#i-hard-drive"/></svg>Storage<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-storage" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/storage" class="">Drives</a></li>
|
||||
<li><a href="/storage/network" class="">Network storage</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-backups"><svg class="ico"><use href="#i-shield"/></svg>Backup<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-backups" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/backups" class="">Overview</a></li>
|
||||
<li><a href="/backups/remote" class="">Remote backup</a></li>
|
||||
<li><a href="/backups/apps" class="">Apps</a></li>
|
||||
<li><a href="/backups/restore" class="">Restore</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-sharing"><svg class="ico"><use href="#i-share"/></svg>Sharing<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-sharing" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/sharing" class="">Network sharing</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/monitoring" class=""><svg class="ico"><use href="#i-cpu"/></svg>System monitor</a></li>
|
||||
<li><a href="/debug" class=""><svg class="ico"><use href="#i-wrench"/></svg>Debug</a></li>
|
||||
</ul>
|
||||
<div class="sidebar-bottom">
|
||||
<div class="nav-group-label">Settings</div>
|
||||
<ul class="nav-links nav-links-sub">
|
||||
<li><a href="/settings" class=""><svg class="ico"><use href="#i-settings"/></svg>System</a></li>
|
||||
<li><a href="/settings/notifications" class=""><svg class="ico"><use href="#i-bell"/></svg>Notifications</a></li>
|
||||
<li><a href="/settings/security" class=""><svg class="ico"><use href="#i-lock"/></svg>Security and access</a></li>
|
||||
</ul>
|
||||
<div class="sidebar-footer">
|
||||
<span class="version">0.261.0</span><details class="lang-globe">
|
||||
<summary class="lang-globe-btn" aria-label="Language" title="Language"><svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10" /><path d="M2 12h20" /><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" /></svg></summary>
|
||||
<ul class="lang-globe-menu">
|
||||
<li><form method="POST" action="/settings/language"><input type="hidden" name="_csrf" value="b0f2927caee41c5a97de77202486f9d0a2d3bb97124581393b77751acb8f4307"><input type="hidden" name="back" value="/apps/tandoor"><button type="submit" name="lang" value="hu" lang="hu" class="lang-globe-item">Magyar</button></form></li>
|
||||
<li><form method="POST" action="/settings/language"><input type="hidden" name="_csrf" value="b0f2927caee41c5a97de77202486f9d0a2d3bb97124581393b77751acb8f4307"><input type="hidden" name="back" value="/apps/tandoor"><button type="submit" name="lang" value="en" lang="en" class="lang-globe-item current" aria-current="true">English</button></form></li>
|
||||
</ul>
|
||||
</details>
|
||||
<a href="/logout" class="logout-link">Sign out ↗</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<main class="content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="alerts-container">
|
||||
|
||||
|
||||
<div class="alert-banner alert-banner-warning">
|
||||
<span class="alert-icon"><svg class="ico"><use href="#i-triangle-alert"/></svg></span>
|
||||
<span class="alert-message">The hub connection is off — central monitoring is not running</span>
|
||||
<a href="/monitoring" class="alert-link">System monitor →</a>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="page-header">
|
||||
<div style="display:flex;align-items:center;gap:1rem">
|
||||
<a href="/stacks" class="btn btn-sm btn-outline">← Apps</a>
|
||||
<h2>Tandoor Recipes</h2>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:.5rem">
|
||||
|
||||
<span class="stack-state-badge state-warn">Not healthy</span>
|
||||
|
||||
|
||||
<span class="tag tag-warn" title="A newer version of this app is available. Select the Update button to start it.">Update available — today</span>
|
||||
<a href="https://recipes.enkisfelhom.hu" target="_blank" class="btn btn-sm btn-outline">Open ↗</a>
|
||||
<a href="/stacks/tandoor/logs" class="btn btn-sm btn-outline">Log</a>
|
||||
|
||||
<a href="/stacks/tandoor/export" class="btn btn-sm btn-outline">Export</a>
|
||||
<a href="/stacks/tandoor/deploy" class="btn btn-sm btn-outline">Settings</a>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-hero">
|
||||
<img class="app-info-logo" src="/static/assets/tandoor-logo.svg"
|
||||
alt="Tandoor Recipes" data-fallback="/static/app-placeholder.svg"
|
||||
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/tandoor-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
||||
<div class="app-info-hero-text">
|
||||
|
||||
<p class="app-info-tagline">A recipe manager and meal planner for the household</p>
|
||||
|
||||
<div class="stack-meta-badges">
|
||||
<span class="meta-badge">~512M RAM</span>
|
||||
<span class="meta-badge">home</span>
|
||||
|
||||
<span class="meta-badge meta-badge-ok">Runs on Pi</span>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="app-screenshots" id="screenshots">
|
||||
<img src="/static/assets/tandoor-screenshot-1.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
<img src="/static/assets/tandoor-screenshot-2.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
<img src="/static/assets/tandoor-screenshot-3.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card" style="margin-top:1rem">
|
||||
<h3>Move to another storage</h3>
|
||||
<p class="form-hint">You can move this app’s data to another connected storage. The app stops briefly during the move, and the data is deleted from the old place only after the check and a successful restart.</p>
|
||||
<div style="display:flex;gap:.5rem;align-items:center;flex-wrap:wrap;margin-top:.5rem">
|
||||
<select id="app-migrate-target" class="btn btn-sm btn-outline">
|
||||
<option value="">Choose a target storage…</option>
|
||||
<option value="/mnt/felhom-drives/scratch_hdd">SCRATCH HDD (/mnt/felhom-drives/scratch_hdd)</option>
|
||||
</select>
|
||||
<button class="btn btn-sm btn-outline" onclick="appMigrate(this,'tandoor','Tandoor Recipes')">Move</button>
|
||||
</div>
|
||||
<div id="app-migrate-progress" style="display:none;margin-top:.75rem;padding:.75rem;border:1px solid var(--blue);border-radius:6px;background:rgba(0,136,204,0.06)"></div>
|
||||
</div>
|
||||
<script>
|
||||
function appMigFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; }
|
||||
function appMigRender(job){
|
||||
var names={stop:'Stopping',copy:'Copying data',verify:'Checking',flip:'Reinstalling',redeploy:'Reinstalling',cleanup:'Deleting old data'};
|
||||
var s=names[job.phase]||job.phase;
|
||||
if(job.phase==='copy'&&job.bytes_total>0){ s+=' ('+Math.floor(100*job.bytes_done/job.bytes_total)+'% — '+appMigFmtGB(job.bytes_done)+'/'+appMigFmtGB(job.bytes_total)+')'; }
|
||||
return s+'…';
|
||||
}
|
||||
function appMigWatch(){
|
||||
var panel=document.getElementById('app-migrate-progress');
|
||||
if(panel) panel.style.display='block';
|
||||
function tick(){
|
||||
fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){
|
||||
var job=d.data&&d.data.job;
|
||||
if(!job){ if(panel) panel.textContent='No move in progress.'; return; }
|
||||
if(panel) panel.innerHTML=appMigRender(job);
|
||||
if(job.phase==='done'){ if(panel) panel.innerHTML+='<br><strong>Done</strong>'; setTimeout(function(){location.reload();},1500); return; }
|
||||
if(job.phase==='aborted'){ if(panel) panel.innerHTML+='<br><strong style="color:var(--danger,#c0392b)">Stopped: '+(job.error||'')+'</strong><br>The old data is untouched.'; return; }
|
||||
setTimeout(tick,1500);
|
||||
}).catch(function(){ setTimeout(tick,2000); });
|
||||
}
|
||||
tick();
|
||||
}
|
||||
function appMigrate(btn,app,label){
|
||||
var sel=document.getElementById('app-migrate-target');
|
||||
var target=sel?sel.value:'';
|
||||
if(!target){ showAlert('Choose a target storage.'); return; }
|
||||
felhomConfirm(btn,'Move the '+label+' data to: '+target+'? The app stops briefly. The old data is deleted only after a successful move.',function(){
|
||||
fetch('/api/storage/migrate-app',{method:'POST',headers:Object.assign({'Content-Type':'application/json'},csrfHeaders()),body:JSON.stringify({app:app,target:target})})
|
||||
.then(function(r){return r.json();}).then(function(d){ if(d.ok){ appMigWatch(); } else { showAlert('Error: '+(d.error||'unknown')); } })
|
||||
.catch(function(e){ showAlert('Error: '+e); });
|
||||
});
|
||||
}
|
||||
(function(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ if(d.data&&d.data.job){ appMigWatch(); } }).catch(function(){}); })();
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<div class="app-info-grid">
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>What is it for?</h3>
|
||||
<ul class="app-info-list">
|
||||
<li>Keep all your recipes in one place</li><li>Import a recipe from a web page with one click</li><li>Plan the week's meals and get a shopping list out of it</li><li>Several people - the household can plan together</li><li>Share a recipe by link with family and friends</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>First steps</h3>
|
||||
<ol class="app-info-list">
|
||||
<li>Open recipes.DOMAIN in your browser</li><li>Create the admin account</li><li>Import your first recipe from a web page (Bookmarklet)</li><li>Try the meal planner</li><li>Invite the household</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>Documentation</h3>
|
||||
<p><a href="https://docs.tandoor.dev/" target="_blank" class="app-info-link">Official documentation ↗</a></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
(function(){
|
||||
var burger=document.querySelector('.nav-burger');
|
||||
var sidebar=document.getElementById('sidebar');
|
||||
var backdrop=document.querySelector('.nav-backdrop');
|
||||
if(!burger||!sidebar||!backdrop)return;
|
||||
function setOpen(open){
|
||||
sidebar.classList.toggle('is-open',open);
|
||||
document.body.classList.toggle('nav-open',open);
|
||||
backdrop.hidden=!open;
|
||||
burger.setAttribute('aria-expanded',open?'true':'false');
|
||||
}
|
||||
burger.addEventListener('click',function(){setOpen(!sidebar.classList.contains('is-open'));});
|
||||
backdrop.addEventListener('click',function(){setOpen(false);});
|
||||
document.addEventListener('keydown',function(e){
|
||||
if(e.key==='Escape'&&sidebar.classList.contains('is-open'))setOpen(false);
|
||||
});
|
||||
})();
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.closest('a, button, .btn, input, select, textarea, .app-row-actions, .stack-detail-actions')) return;
|
||||
var card = e.target.closest('[data-href]');
|
||||
if (card) window.location.href = card.dataset.href;
|
||||
});
|
||||
async function checkBeforeDeploy(e, name) {
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name);
|
||||
var data = await resp.json();
|
||||
if (data.ok && data.data && data.data.deployed) {
|
||||
e.preventDefault();
|
||||
showAlert('This app is already installed.');
|
||||
window.location.reload();
|
||||
return false;
|
||||
}
|
||||
} catch(err) {}
|
||||
return true;
|
||||
}
|
||||
async function syncTemplates() {
|
||||
const btn = document.getElementById('sync-btn');
|
||||
const toast = document.getElementById('sync-toast');
|
||||
if (!btn) return;
|
||||
const origText = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '↻ Refreshing...';
|
||||
btn.classList.add('loading');
|
||||
try {
|
||||
const resp = await fetch('/api/sync', {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders())
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (toast) {
|
||||
toast.textContent = data.ok ? (data.message || 'Templates refreshed') : ('Error: ' + (data.error || 'Unknown error'));
|
||||
toast.className = 'sync-toast ' + (data.ok ? 'sync-toast-ok' : 'sync-toast-err');
|
||||
toast.style.display = 'block';
|
||||
setTimeout(function() { toast.style.display = 'none'; }, 5000);
|
||||
}
|
||||
if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) {
|
||||
setTimeout(function() { window.location.reload(); }, 1500);
|
||||
}
|
||||
} catch (err) {
|
||||
if (toast) {
|
||||
toast.textContent = 'Network error: ' + err.message;
|
||||
toast.className = 'sync-toast sync-toast-err';
|
||||
toast.style.display = 'block';
|
||||
setTimeout(function() { toast.style.display = 'none'; }, 5000);
|
||||
}
|
||||
}
|
||||
btn.innerHTML = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
}
|
||||
async function stackAction(event, name, action) {
|
||||
const btn = event.currentTarget;
|
||||
const origText = btn.textContent;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'In progress...';
|
||||
btn.classList.add('loading');
|
||||
try {
|
||||
const resp = await fetch('/api/stacks/' + name + '/' + action, {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders())
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (!data.ok) {
|
||||
showAlert('Error: ' + (data.error || 'Unknown error'));
|
||||
btn.textContent = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (action === 'update') {
|
||||
followUpdate(name, btn);
|
||||
return;
|
||||
}
|
||||
window.location.reload();
|
||||
} catch (err) {
|
||||
showAlert('Network error: ' + err.message);
|
||||
btn.textContent = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
}
|
||||
}
|
||||
function followUpdate(name, btn) {
|
||||
var started = Date.now();
|
||||
var timer = setInterval(async function() {
|
||||
if (Date.now() - started > 30 * 60 * 1000) { clearInterval(timer); window.location.reload(); return; }
|
||||
try {
|
||||
var r = await fetch('/api/stacks/' + name);
|
||||
var d = await r.json();
|
||||
if (!d.ok || !d.data) return;
|
||||
if (d.data.update_phase_label) btn.textContent = d.data.update_phase_label;
|
||||
if (!d.data.updating) { clearInterval(timer); window.location.reload(); }
|
||||
} catch (e) {}
|
||||
}, 3000);
|
||||
}
|
||||
async function deleteOrphanStack(name) {
|
||||
var modal = document.createElement('div');
|
||||
modal.className = 'modal-overlay';
|
||||
modal.id = 'delete-modal';
|
||||
modal.innerHTML = '<div class="modal-card"><h3>Loading...</h3></div>';
|
||||
modal.addEventListener('click', function(e) { if (e.target === modal) closeDeleteModal(); });
|
||||
document.body.appendChild(modal);
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name + '/hdd-data');
|
||||
var data = await resp.json();
|
||||
var hddInfo = '';
|
||||
var checkboxHTML = '';
|
||||
if (data.ok && data.data && data.data.has_hdd_data) {
|
||||
hddInfo = '<div class="modal-hdd-info"><strong>Your data on the hard drive:</strong>';
|
||||
data.data.hdd_paths.forEach(function(p) {
|
||||
hddInfo += '<div class="modal-hdd-path">' + p.path + ' (' + (p.exists ? p.size_human : 'does not exist') + ')</div>';
|
||||
});
|
||||
hddInfo += '</div>';
|
||||
checkboxHTML = '<label class="modal-checkbox"><input type="checkbox" id="delete-hdd-check"> Delete your data from the hard drive</label>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Delete app: ' + name + '</h3>' +
|
||||
'<p style="color:var(--text-2);font-size:.9rem;margin-bottom:.75rem">This removes the containers, the volumes and the configuration files.</p>' +
|
||||
'<div class="alert alert-warning" style="margin-bottom:.75rem">You cannot undo this!</div>' +
|
||||
hddInfo + checkboxHTML +
|
||||
'<div class="modal-actions">' +
|
||||
'<button class="btn btn-outline" onclick="closeDeleteModal()">Cancel</button>' +
|
||||
'<button class="btn btn-danger" id="confirm-delete-btn" onclick="confirmDelete(\'' + name + '\')">Delete</button>' +
|
||||
'</div>';
|
||||
} catch (err) {
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Error</h3><p style="color:var(--text-2)">Could not load the details: ' + err.message + '</p>' +
|
||||
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeDeleteModal()">Close</button></div>';
|
||||
}
|
||||
}
|
||||
function closeDeleteModal() {
|
||||
var modal = document.getElementById('delete-modal');
|
||||
if (modal) modal.remove();
|
||||
}
|
||||
async function confirmDelete(name) {
|
||||
var btn = document.getElementById('confirm-delete-btn');
|
||||
var checkbox = document.getElementById('delete-hdd-check');
|
||||
var removeHDD = checkbox ? checkbox.checked : false;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Deleting...';
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name, {
|
||||
method: 'DELETE',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({remove_hdd_data: removeHDD})
|
||||
});
|
||||
var data = await resp.json();
|
||||
if (data.ok) {
|
||||
var modal = document.getElementById('delete-modal');
|
||||
var removedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) {
|
||||
removedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Deleted data: ' + data.data.hdd_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
var preservedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) {
|
||||
preservedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Kept data: ' + data.data.hdd_paths_preserved.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.hdd_note) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">' + data.data.hdd_note + '</p>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Deleted!</h3>' +
|
||||
'<p style="color:var(--text-2)">The app (' + name + ') was deleted.</p>' +
|
||||
removedInfo + preservedInfo +
|
||||
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Close</button></div>';
|
||||
} else {
|
||||
showAlert('Error: ' + (data.error || 'Unknown error'));
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Delete';
|
||||
}
|
||||
} catch (err) {
|
||||
showAlert('Network error: ' + err.message);
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Delete';
|
||||
}
|
||||
}
|
||||
async function removeStack(name) {
|
||||
var modal = document.createElement('div');
|
||||
modal.className = 'modal-overlay';
|
||||
modal.id = 'remove-modal';
|
||||
modal.innerHTML = '<div class="modal-card"><h3>Loading...</h3></div>';
|
||||
modal.addEventListener('click', function(e) { if (e.target === modal) closeRemoveModal(); });
|
||||
document.body.appendChild(modal);
|
||||
try {
|
||||
var [hddResp, backupResp] = await Promise.all([
|
||||
fetch('/api/stacks/' + name + '/hdd-data').then(function(r) { return r.json(); }),
|
||||
fetch('/api/stacks/' + name + '/backup-data').then(function(r) { return r.json(); })
|
||||
]);
|
||||
var sections = '';
|
||||
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Always deleted:</strong>' +
|
||||
'<ul style="margin:.25rem 0;padding-left:1.2rem;color:var(--text-2);font-size:.85rem">' +
|
||||
'<li>Docker volumes (database, app configuration)</li>' +
|
||||
'<li>Install configuration (app.yaml)</li>' +
|
||||
'<li>Secondary backup schedule</li>' +
|
||||
'</ul></div>';
|
||||
|
||||
var hddCheckbox = '';
|
||||
if (hddResp.ok && hddResp.data && hddResp.data.has_hdd_data) {
|
||||
var hddPaths = '';
|
||||
hddResp.data.hdd_paths.forEach(function(p) {
|
||||
hddPaths += '<div class="modal-hdd-path">' + p.path + ' (' + (p.exists ? p.size_human : 'does not exist') + ')</div>';
|
||||
});
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Your data on the hard drive:</strong>' + hddPaths +
|
||||
'<label class="modal-checkbox"><input type="checkbox" id="remove-hdd-check"> Delete your data</label>' +
|
||||
'<div class="alert alert-info" style="margin-top:.5rem;font-size:.8rem" id="remove-hdd-keep-warning">' +
|
||||
'If you reinstall the app, you need to import the data again, because the database is deleted. Data you keep is NOT backed up automatically any more.' +
|
||||
'</div></div>';
|
||||
hddCheckbox = '\x3Cscript>document.getElementById("remove-hdd-check").addEventListener("change",function(){document.getElementById("remove-hdd-keep-warning").style.display=this.checked?"none":"";});<\/script>';
|
||||
}
|
||||
|
||||
var backupCheckbox = '';
|
||||
if (backupResp.ok && backupResp.data && backupResp.data.has_backups) {
|
||||
var bkPaths = '';
|
||||
backupResp.data.backup_paths.forEach(function(p) {
|
||||
if (p.exists) bkPaths += '<div class="modal-hdd-path">' + p.path + ' (' + p.size_human + ')</div>';
|
||||
});
|
||||
if (bkPaths) {
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Backup data:</strong>' + bkPaths +
|
||||
'<label class="modal-checkbox"><input type="checkbox" id="remove-backup-check"> Delete backup data</label>' +
|
||||
'<div class="alert alert-info" style="margin-top:.5rem;font-size:.8rem">' +
|
||||
'The nightly restic snapshots cannot be deleted one by one — they expire automatically under the retention rules.' +
|
||||
'</div></div>';
|
||||
}
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Remove app: ' + name + '</h3>' +
|
||||
'<p style="color:var(--text-2);font-size:.9rem;margin-bottom:.75rem">The app goes back to "Not installed". The template stays, and you can install it again.</p>' +
|
||||
'<div class="alert alert-warning" style="margin-bottom:.75rem">You cannot undo this!</div>' +
|
||||
sections +
|
||||
'<div class="modal-actions">' +
|
||||
'<button class="btn btn-outline" onclick="closeRemoveModal()">Cancel</button>' +
|
||||
'<button class="btn btn-danger" id="confirm-remove-btn" onclick="confirmRemoveStack(\'' + name + '\')">Remove</button>' +
|
||||
'</div>' + hddCheckbox;
|
||||
} catch (err) {
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Error</h3><p style="color:var(--text-2)">Could not load the details: ' + err.message + '</p>' +
|
||||
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeRemoveModal()">Close</button></div>';
|
||||
}
|
||||
}
|
||||
function closeRemoveModal() {
|
||||
var modal = document.getElementById('remove-modal');
|
||||
if (modal) modal.remove();
|
||||
}
|
||||
async function confirmRemoveStack(name) {
|
||||
var btn = document.getElementById('confirm-remove-btn');
|
||||
var hddCheck = document.getElementById('remove-hdd-check');
|
||||
var backupCheck = document.getElementById('remove-backup-check');
|
||||
var removeHDD = hddCheck ? hddCheck.checked : false;
|
||||
var removeBackups = backupCheck ? backupCheck.checked : false;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Removing...';
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name + '/remove', {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({remove_hdd_data: removeHDD, remove_backups: removeBackups})
|
||||
});
|
||||
var data = await resp.json();
|
||||
if (data.ok) {
|
||||
var modal = document.getElementById('remove-modal');
|
||||
var removedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) {
|
||||
removedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Deleted data: ' + data.data.hdd_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.backup_paths_removed && data.data.backup_paths_removed.length > 0) {
|
||||
removedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Deleted backups: ' + data.data.backup_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
var preservedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) {
|
||||
preservedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Kept data: ' + data.data.hdd_paths_preserved.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.hdd_note) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">' + data.data.hdd_note + '</p>';
|
||||
}
|
||||
if (data.data && data.data.backup_paths_refused && data.data.backup_paths_refused.length > 0) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Backups not deleted: ' + data.data.backup_paths_refused.join('; ') + '</p>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Removed!</h3>' +
|
||||
'<p style="color:var(--text-2)">The app (' + name + ') was removed. You can install it again with the Install button.</p>' +
|
||||
removedInfo + preservedInfo +
|
||||
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Close</button></div>';
|
||||
} else {
|
||||
showAlert('Error: ' + (data.error || 'Unknown error'));
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Remove';
|
||||
}
|
||||
} catch (err) {
|
||||
showAlert('Network error: ' + err.message);
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Remove';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,654 @@
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="hu" class="no-js">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Tandoor Recipes — Felhom.eu</title>
|
||||
|
||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg?v=0.261.0">
|
||||
<link rel="stylesheet" href="/static/style.css?v=0.261.0">
|
||||
<meta name="csrf-token" content="b0f2927caee41c5a97de77202486f9d0a2d3bb97124581393b77751acb8f4307">
|
||||
<script>
|
||||
|
||||
|
||||
document.documentElement.className=document.documentElement.className.replace('no-js','js');
|
||||
function csrfHeaders(){var el=document.querySelector('meta[name="csrf-token"]');return el?{'X-CSRF-Token':el.content}:{};}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function felhomConfirm(el,question,onYes){
|
||||
if(!el||el.dataset.fcOpen)return;
|
||||
el.dataset.fcOpen='1';
|
||||
var wrap=document.createElement('span');wrap.className='inline-confirm';
|
||||
var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question;
|
||||
var yes=document.createElement('button');yes.type='button';yes.className='btn btn-xs btn-danger';yes.textContent='Igen';
|
||||
var no=document.createElement('button');no.type='button';no.className='btn btn-xs btn-outline';no.textContent='Mégse';
|
||||
wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no);
|
||||
el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling);
|
||||
function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;}
|
||||
no.addEventListener('click',close);
|
||||
yes.addEventListener('click',function(){close();onYes();});
|
||||
}
|
||||
document.addEventListener('click',function(e){
|
||||
var btn=e.target.closest?e.target.closest('[data-confirm]'):null;
|
||||
if(!btn)return;
|
||||
e.preventDefault();
|
||||
felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){
|
||||
var form=btn.closest('form');
|
||||
if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
document.addEventListener('click',function(e){
|
||||
var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null;
|
||||
if(!btn)return;
|
||||
var group=btn.closest('.nav-group');
|
||||
if(!group)return;
|
||||
var willOpen=!group.classList.contains('is-open');
|
||||
document.querySelectorAll('.nav-group.is-open').forEach(function(g){
|
||||
g.classList.remove('is-open');
|
||||
var t=g.querySelector('.nav-group-toggle');
|
||||
if(t)t.setAttribute('aria-expanded','false');
|
||||
});
|
||||
if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');}
|
||||
});
|
||||
function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML='<h3>Üzenet</h3><pre style="white-space:pre-wrap;word-break:break-word;background:var(--bg-2);padding:.75rem;border-radius:.375rem;font-size:.85rem;max-height:60vh;overflow-y:auto;user-select:text;cursor:text">'+msg.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')+'</pre><div class="modal-actions"><button class="btn btn-primary" onclick="document.getElementById(\'alert-modal\').remove()">OK</button></div>';o.appendChild(c);document.body.appendChild(o);}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display:none" aria-hidden="true">
|
||||
<symbol id="i-hard-drive" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 16h.01" /> <path d="M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" /> <path d="M21.946 12.013H2.054" /> <path d="M6 16h.01" /></symbol>
|
||||
<symbol id="i-cpu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20v2" /> <path d="M12 2v2" /> <path d="M17 20v2" /> <path d="M17 2v2" /> <path d="M2 12h2" /> <path d="M2 17h2" /> <path d="M2 7h2" /> <path d="M20 12h2" /> <path d="M20 17h2" /> <path d="M20 7h2" /> <path d="M7 20v2" /> <path d="M7 2v2" /> <rect x="4" y="4" width="16" height="16" rx="2" /> <rect x="8" y="8" width="8" height="8" rx="1" /></symbol>
|
||||
<symbol id="i-memory-stick" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 12v-2" /> <path d="M12 18v-2" /> <path d="M16 12v-2" /> <path d="M16 18v-2" /> <path d="M2 11h1.5" /> <path d="M20 18v-2" /> <path d="M20.5 11H22" /> <path d="M4 18v-2" /> <path d="M8 12v-2" /> <path d="M8 18v-2" /> <rect x="2" y="6" width="20" height="10" rx="2" /></symbol>
|
||||
<symbol id="i-thermometer" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 4v10.54a4 4 0 1 1-4 0V4a2 2 0 0 1 4 0Z" /></symbol>
|
||||
<symbol id="i-triangle-alert" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" /> <path d="M12 9v4" /> <path d="M12 17h.01" /></symbol>
|
||||
<symbol id="i-lock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="11" x="3" y="11" rx="2" ry="2" /> <path d="M7 11V7a5 5 0 0 1 10 0v4" /></symbol>
|
||||
<symbol id="i-cloud" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z" /></symbol>
|
||||
<symbol id="i-square" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" /></symbol>
|
||||
<symbol id="i-rotate-cw" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" /> <path d="M21 3v5h-5" /></symbol>
|
||||
<symbol id="i-file-text" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /> <path d="M14 2v5a1 1 0 0 0 1 1h5" /> <path d="M10 9H8" /> <path d="M16 13H8" /> <path d="M16 17H8" /></symbol>
|
||||
<symbol id="i-external-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6" /> <path d="M10 14 21 3" /> <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></symbol>
|
||||
<symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /></symbol>
|
||||
<symbol id="i-server" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="8" x="2" y="2" rx="2" ry="2" /> <rect width="20" height="8" x="2" y="14" rx="2" ry="2" /> <line x1="6" x2="6.01" y1="6" y2="6" /> <line x1="6" x2="6.01" y1="18" y2="18" /></symbol>
|
||||
<symbol id="i-share" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3" /> <circle cx="6" cy="12" r="3" /> <circle cx="18" cy="19" r="3" /> <line x1="8.59" x2="15.42" y1="13.51" y2="17.49" /> <line x1="15.41" x2="8.59" y1="6.51" y2="10.49" /></symbol>
|
||||
<symbol id="i-layout-grid" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="7" x="3" y="3" rx="1" /> <rect width="7" height="7" x="14" y="3" rx="1" /> <rect width="7" height="7" x="14" y="14" rx="1" /> <rect width="7" height="7" x="3" y="14" rx="1" /></symbol>
|
||||
<symbol id="i-rocket" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91 0z" /> <path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z" /> <path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0" /> <path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5" /></symbol>
|
||||
<symbol id="i-settings" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /> <circle cx="12" cy="12" r="3" /></symbol>
|
||||
<symbol id="i-bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.268 21a2 2 0 0 0 3.464 0" /> <path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" /></symbol>
|
||||
<symbol id="i-x" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18" /> <path d="m6 6 12 12" /></symbol>
|
||||
<symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5" /></symbol>
|
||||
<symbol id="i-pencil" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" /> <path d="m15 5 4 4" /></symbol>
|
||||
<symbol id="i-info" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /> <path d="M12 16v-4" /> <path d="M12 8h.01" /></symbol>
|
||||
<symbol id="i-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 15V3" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /> <path d="m7 10 5 5 5-5" /></symbol>
|
||||
<symbol id="i-upload" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v12" /> <path d="m17 8-5-5-5 5" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /></symbol>
|
||||
<symbol id="i-trash-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 11v6" /> <path d="M14 11v6" /> <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /> <path d="M3 6h18" /> <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></symbol>
|
||||
<symbol id="i-wrench" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z" /></symbol>
|
||||
<symbol id="i-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" /> <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" /></symbol>
|
||||
<symbol id="i-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34" /> <circle cx="11" cy="11" r="8" /></symbol>
|
||||
<symbol id="i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14" /> <path d="M12 5v14" /></symbol>
|
||||
<symbol id="i-chevron-down" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6" /></symbol>
|
||||
<symbol id="i-play" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z" /></symbol>
|
||||
<symbol id="i-pause" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="14" y="3" width="5" height="18" rx="1" /> <rect x="5" y="3" width="5" height="18" rx="1" /></symbol>
|
||||
<symbol id="i-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h16" /> <path d="M4 6h16" /> <path d="M4 18h16" /></symbol>
|
||||
</svg>
|
||||
<header class="mobile-topbar">
|
||||
<a href="/" class="mobile-topbar-logo"><img src="/static/felhom-logo.svg?v=0.261.0" alt="Felhom.eu"></a>
|
||||
<button type="button" class="nav-burger" aria-expanded="false" aria-controls="sidebar" aria-label="Menü">
|
||||
<svg class="ico"><use href="#i-menu"/></svg>
|
||||
</button>
|
||||
</header>
|
||||
<div class="nav-backdrop" hidden></div>
|
||||
<nav class="sidebar" id="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<img src="/static/felhom-logo.svg?v=0.261.0" alt="Felhom.eu" class="sidebar-logo">
|
||||
</div>
|
||||
<ul class="nav-links">
|
||||
<li><a href="/launcher" class=""><svg class="ico"><use href="#i-rocket"/></svg>Indítópult</a></li>
|
||||
<li><a href="/dashboard" class=""><svg class="ico"><use href="#i-layout-grid"/></svg>Vezérlőpult</a></li>
|
||||
<li><a href="/stacks" class="active"><svg class="ico"><use href="#i-cloud"/></svg>Alkalmazások</a></li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-storage"><svg class="ico"><use href="#i-hard-drive"/></svg>Tárhely<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-storage" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/storage" class="">Meghajtók</a></li>
|
||||
<li><a href="/storage/network" class="">Hálózati tárhely</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-backups"><svg class="ico"><use href="#i-shield"/></svg>Biztonsági mentés<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-backups" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/backups" class="">Áttekintés</a></li>
|
||||
<li><a href="/backups/remote" class="">Távoli mentés</a></li>
|
||||
<li><a href="/backups/apps" class="">Alkalmazások</a></li>
|
||||
<li><a href="/backups/restore" class="">Visszaállítás</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-sharing"><svg class="ico"><use href="#i-share"/></svg>Megosztás<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-sharing" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/sharing" class="">Hálózati megosztás</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/monitoring" class=""><svg class="ico"><use href="#i-cpu"/></svg>Rendszermonitor</a></li>
|
||||
<li><a href="/debug" class=""><svg class="ico"><use href="#i-wrench"/></svg>Debug</a></li>
|
||||
</ul>
|
||||
<div class="sidebar-bottom">
|
||||
<div class="nav-group-label">Beállítások</div>
|
||||
<ul class="nav-links nav-links-sub">
|
||||
<li><a href="/settings" class=""><svg class="ico"><use href="#i-settings"/></svg>Rendszer</a></li>
|
||||
<li><a href="/settings/notifications" class=""><svg class="ico"><use href="#i-bell"/></svg>Értesítések</a></li>
|
||||
<li><a href="/settings/security" class=""><svg class="ico"><use href="#i-lock"/></svg>Biztonság és hozzáférés</a></li>
|
||||
</ul>
|
||||
<div class="sidebar-footer">
|
||||
<span class="version">0.261.0</span><details class="lang-globe">
|
||||
<summary class="lang-globe-btn" aria-label="Nyelv" title="Nyelv"><svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10" /><path d="M2 12h20" /><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" /></svg></summary>
|
||||
<ul class="lang-globe-menu">
|
||||
<li><form method="POST" action="/settings/language"><input type="hidden" name="_csrf" value="b0f2927caee41c5a97de77202486f9d0a2d3bb97124581393b77751acb8f4307"><input type="hidden" name="back" value="/apps/tandoor"><button type="submit" name="lang" value="hu" lang="hu" class="lang-globe-item current" aria-current="true">Magyar</button></form></li>
|
||||
<li><form method="POST" action="/settings/language"><input type="hidden" name="_csrf" value="b0f2927caee41c5a97de77202486f9d0a2d3bb97124581393b77751acb8f4307"><input type="hidden" name="back" value="/apps/tandoor"><button type="submit" name="lang" value="en" lang="en" class="lang-globe-item">English</button></form></li>
|
||||
</ul>
|
||||
</details>
|
||||
<a href="/logout" class="logout-link">Kijelentkezés ↗</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<main class="content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="alerts-container">
|
||||
|
||||
|
||||
<div class="alert-banner alert-banner-warning">
|
||||
<span class="alert-icon"><svg class="ico"><use href="#i-triangle-alert"/></svg></span>
|
||||
<span class="alert-message">Hub kapcsolat kikapcsolva — a központi monitoring nem aktív</span>
|
||||
<a href="/monitoring" class="alert-link">Rendszermonitor →</a>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="page-header">
|
||||
<div style="display:flex;align-items:center;gap:1rem">
|
||||
<a href="/stacks" class="btn btn-sm btn-outline">← Alkalmazások</a>
|
||||
<h2>Tandoor Recipes</h2>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:.5rem">
|
||||
|
||||
<span class="stack-state-badge state-warn">Nem egészséges</span>
|
||||
|
||||
|
||||
<span class="tag tag-warn" title="Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.">Frissítés elérhető — ma</span>
|
||||
<a href="https://recipes.enkisfelhom.hu" target="_blank" class="btn btn-sm btn-outline">Megnyitás ↗</a>
|
||||
<a href="/stacks/tandoor/logs" class="btn btn-sm btn-outline">Napló</a>
|
||||
|
||||
<a href="/stacks/tandoor/export" class="btn btn-sm btn-outline">Exportálás</a>
|
||||
<a href="/stacks/tandoor/deploy" class="btn btn-sm btn-outline">Beállítások</a>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-hero">
|
||||
<img class="app-info-logo" src="/static/assets/tandoor-logo.svg"
|
||||
alt="Tandoor Recipes" data-fallback="/static/app-placeholder.svg"
|
||||
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/tandoor-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
||||
<div class="app-info-hero-text">
|
||||
|
||||
<p class="app-info-tagline">Receptkezelő és étkezés tervező a családnak</p>
|
||||
|
||||
<div class="stack-meta-badges">
|
||||
<span class="meta-badge">~512M RAM</span>
|
||||
<span class="meta-badge">home</span>
|
||||
|
||||
<span class="meta-badge meta-badge-ok">Pi kompatibilis</span>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="app-screenshots" id="screenshots">
|
||||
<img src="/static/assets/tandoor-screenshot-1.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
<img src="/static/assets/tandoor-screenshot-2.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
<img src="/static/assets/tandoor-screenshot-3.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card" style="margin-top:1rem">
|
||||
<h3>Áthelyezés másik tárhelyre</h3>
|
||||
<p class="form-hint">Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át. Az alkalmazás az áthelyezés alatt rövid időre leáll, az adatok pedig csak az ellenőrzés és a sikeres újraindítás után törlődnek a régi helyről.</p>
|
||||
<div style="display:flex;gap:.5rem;align-items:center;flex-wrap:wrap;margin-top:.5rem">
|
||||
<select id="app-migrate-target" class="btn btn-sm btn-outline">
|
||||
<option value="">Válassz céltárhelyet…</option>
|
||||
<option value="/mnt/felhom-drives/scratch_hdd">SCRATCH HDD (/mnt/felhom-drives/scratch_hdd)</option>
|
||||
</select>
|
||||
<button class="btn btn-sm btn-outline" onclick="appMigrate(this,'tandoor','Tandoor Recipes')">Áthelyezés</button>
|
||||
</div>
|
||||
<div id="app-migrate-progress" style="display:none;margin-top:.75rem;padding:.75rem;border:1px solid var(--blue);border-radius:6px;background:rgba(0,136,204,0.06)"></div>
|
||||
</div>
|
||||
<script>
|
||||
function appMigFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; }
|
||||
function appMigRender(job){
|
||||
var names={stop:'Leállítás',copy:'Adatok másolása',verify:'Ellenőrzés',flip:'Újratelepítés',redeploy:'Újratelepítés',cleanup:'Régi adatok törlése'};
|
||||
var s=names[job.phase]||job.phase;
|
||||
if(job.phase==='copy'&&job.bytes_total>0){ s+=' ('+Math.floor(100*job.bytes_done/job.bytes_total)+'% — '+appMigFmtGB(job.bytes_done)+'/'+appMigFmtGB(job.bytes_total)+')'; }
|
||||
return s+'…';
|
||||
}
|
||||
function appMigWatch(){
|
||||
var panel=document.getElementById('app-migrate-progress');
|
||||
if(panel) panel.style.display='block';
|
||||
function tick(){
|
||||
fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){
|
||||
var job=d.data&&d.data.job;
|
||||
if(!job){ if(panel) panel.textContent='Nincs folyamatban áthelyezés.'; return; }
|
||||
if(panel) panel.innerHTML=appMigRender(job);
|
||||
if(job.phase==='done'){ if(panel) panel.innerHTML+='<br><strong>Kész</strong>'; setTimeout(function(){location.reload();},1500); return; }
|
||||
if(job.phase==='aborted'){ if(panel) panel.innerHTML+='<br><strong style="color:var(--danger,#c0392b)">Megszakadt: '+(job.error||'')+'</strong><br>A régi adatok érintetlenek.'; return; }
|
||||
setTimeout(tick,1500);
|
||||
}).catch(function(){ setTimeout(tick,2000); });
|
||||
}
|
||||
tick();
|
||||
}
|
||||
function appMigrate(btn,app,label){
|
||||
var sel=document.getElementById('app-migrate-target');
|
||||
var target=sel?sel.value:'';
|
||||
if(!target){ showAlert('Válassz céltárhelyet.'); return; }
|
||||
felhomConfirm(btn,'Áthelyezed a(z) '+label+' adatait ide: '+target+'? Az alkalmazás rövid időre leáll. A régi adatok csak sikeres áthelyezés után törlődnek.',function(){
|
||||
fetch('/api/storage/migrate-app',{method:'POST',headers:Object.assign({'Content-Type':'application/json'},csrfHeaders()),body:JSON.stringify({app:app,target:target})})
|
||||
.then(function(r){return r.json();}).then(function(d){ if(d.ok){ appMigWatch(); } else { showAlert('Hiba: '+(d.error||'ismeretlen')); } })
|
||||
.catch(function(e){ showAlert('Hiba: '+e); });
|
||||
});
|
||||
}
|
||||
(function(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ if(d.data&&d.data.job){ appMigWatch(); } }).catch(function(){}); })();
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<div class="app-info-grid">
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>Mire használható?</h3>
|
||||
<ul class="app-info-list">
|
||||
<li>Receptek gyűjtése és rendszerezése egy helyen</li><li>Receptek importálása weboldalakról egy kattintással</li><li>Heti étkezés tervezés és bevásárlólista generálás</li><li>Több felhasználó - a család együtt tervezhet</li><li>Receptek megosztása linkkel családtagokkal, barátokkal</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>Első lépések</h3>
|
||||
<ol class="app-info-list">
|
||||
<li>Nyisd meg a recipes.DOMAIN címet a böngészőben</li><li>Hozd létre az admin fiókot</li><li>Importáld az első receptet egy weboldalról (Bookmarklet)</li><li>Próbáld ki az étkezés tervezőt</li><li>Hívd meg a családtagokat</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>Dokumentáció</h3>
|
||||
<p><a href="https://docs.tandoor.dev/" target="_blank" class="app-info-link">Hivatalos dokumentáció ↗</a></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
(function(){
|
||||
var burger=document.querySelector('.nav-burger');
|
||||
var sidebar=document.getElementById('sidebar');
|
||||
var backdrop=document.querySelector('.nav-backdrop');
|
||||
if(!burger||!sidebar||!backdrop)return;
|
||||
function setOpen(open){
|
||||
sidebar.classList.toggle('is-open',open);
|
||||
document.body.classList.toggle('nav-open',open);
|
||||
backdrop.hidden=!open;
|
||||
burger.setAttribute('aria-expanded',open?'true':'false');
|
||||
}
|
||||
burger.addEventListener('click',function(){setOpen(!sidebar.classList.contains('is-open'));});
|
||||
backdrop.addEventListener('click',function(){setOpen(false);});
|
||||
document.addEventListener('keydown',function(e){
|
||||
if(e.key==='Escape'&&sidebar.classList.contains('is-open'))setOpen(false);
|
||||
});
|
||||
})();
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.closest('a, button, .btn, input, select, textarea, .app-row-actions, .stack-detail-actions')) return;
|
||||
var card = e.target.closest('[data-href]');
|
||||
if (card) window.location.href = card.dataset.href;
|
||||
});
|
||||
async function checkBeforeDeploy(e, name) {
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name);
|
||||
var data = await resp.json();
|
||||
if (data.ok && data.data && data.data.deployed) {
|
||||
e.preventDefault();
|
||||
showAlert('Ez az alkalmazás már telepítve van.');
|
||||
window.location.reload();
|
||||
return false;
|
||||
}
|
||||
} catch(err) {}
|
||||
return true;
|
||||
}
|
||||
async function syncTemplates() {
|
||||
const btn = document.getElementById('sync-btn');
|
||||
const toast = document.getElementById('sync-toast');
|
||||
if (!btn) return;
|
||||
const origText = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '↻ Frissítés...';
|
||||
btn.classList.add('loading');
|
||||
try {
|
||||
const resp = await fetch('/api/sync', {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders())
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (toast) {
|
||||
toast.textContent = data.ok ? (data.message || 'Sablonok frissítve') : ('Hiba: ' + (data.error || 'Ismeretlen hiba'));
|
||||
toast.className = 'sync-toast ' + (data.ok ? 'sync-toast-ok' : 'sync-toast-err');
|
||||
toast.style.display = 'block';
|
||||
setTimeout(function() { toast.style.display = 'none'; }, 5000);
|
||||
}
|
||||
if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) {
|
||||
setTimeout(function() { window.location.reload(); }, 1500);
|
||||
}
|
||||
} catch (err) {
|
||||
if (toast) {
|
||||
toast.textContent = 'Hálózati hiba: ' + err.message;
|
||||
toast.className = 'sync-toast sync-toast-err';
|
||||
toast.style.display = 'block';
|
||||
setTimeout(function() { toast.style.display = 'none'; }, 5000);
|
||||
}
|
||||
}
|
||||
btn.innerHTML = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
}
|
||||
async function stackAction(event, name, action) {
|
||||
const btn = event.currentTarget;
|
||||
const origText = btn.textContent;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Folyamatban...';
|
||||
btn.classList.add('loading');
|
||||
try {
|
||||
const resp = await fetch('/api/stacks/' + name + '/' + action, {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders())
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (!data.ok) {
|
||||
showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba'));
|
||||
btn.textContent = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (action === 'update') {
|
||||
followUpdate(name, btn);
|
||||
return;
|
||||
}
|
||||
window.location.reload();
|
||||
} catch (err) {
|
||||
showAlert('Hálózati hiba: ' + err.message);
|
||||
btn.textContent = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
}
|
||||
}
|
||||
function followUpdate(name, btn) {
|
||||
var started = Date.now();
|
||||
var timer = setInterval(async function() {
|
||||
if (Date.now() - started > 30 * 60 * 1000) { clearInterval(timer); window.location.reload(); return; }
|
||||
try {
|
||||
var r = await fetch('/api/stacks/' + name);
|
||||
var d = await r.json();
|
||||
if (!d.ok || !d.data) return;
|
||||
if (d.data.update_phase_label) btn.textContent = d.data.update_phase_label;
|
||||
if (!d.data.updating) { clearInterval(timer); window.location.reload(); }
|
||||
} catch (e) {}
|
||||
}, 3000);
|
||||
}
|
||||
async function deleteOrphanStack(name) {
|
||||
var modal = document.createElement('div');
|
||||
modal.className = 'modal-overlay';
|
||||
modal.id = 'delete-modal';
|
||||
modal.innerHTML = '<div class="modal-card"><h3>Betöltés...</h3></div>';
|
||||
modal.addEventListener('click', function(e) { if (e.target === modal) closeDeleteModal(); });
|
||||
document.body.appendChild(modal);
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name + '/hdd-data');
|
||||
var data = await resp.json();
|
||||
var hddInfo = '';
|
||||
var checkboxHTML = '';
|
||||
if (data.ok && data.data && data.data.has_hdd_data) {
|
||||
hddInfo = '<div class="modal-hdd-info"><strong>Felhasználói adatok a merevlemezen:</strong>';
|
||||
data.data.hdd_paths.forEach(function(p) {
|
||||
hddInfo += '<div class="modal-hdd-path">' + p.path + ' (' + (p.exists ? p.size_human : 'nem létezik') + ')</div>';
|
||||
});
|
||||
hddInfo += '</div>';
|
||||
checkboxHTML = '<label class="modal-checkbox"><input type="checkbox" id="delete-hdd-check"> Felhasználói adatok törlése a merevlemezről</label>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Alkalmazás törlése: ' + name + '</h3>' +
|
||||
'<p style="color:var(--text-2);font-size:.9rem;margin-bottom:.75rem">Ez a művelet eltávolítja a konténereket, a köteteket és a konfigurációs fájlokat.</p>' +
|
||||
'<div class="alert alert-warning" style="margin-bottom:.75rem">Ez a művelet nem visszavonható!</div>' +
|
||||
hddInfo + checkboxHTML +
|
||||
'<div class="modal-actions">' +
|
||||
'<button class="btn btn-outline" onclick="closeDeleteModal()">Mégsem</button>' +
|
||||
'<button class="btn btn-danger" id="confirm-delete-btn" onclick="confirmDelete(\'' + name + '\')">Törlés</button>' +
|
||||
'</div>';
|
||||
} catch (err) {
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Hiba</h3><p style="color:var(--text-2)">Nem sikerült lekérni az adatokat: ' + err.message + '</p>' +
|
||||
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeDeleteModal()">Bezárás</button></div>';
|
||||
}
|
||||
}
|
||||
function closeDeleteModal() {
|
||||
var modal = document.getElementById('delete-modal');
|
||||
if (modal) modal.remove();
|
||||
}
|
||||
async function confirmDelete(name) {
|
||||
var btn = document.getElementById('confirm-delete-btn');
|
||||
var checkbox = document.getElementById('delete-hdd-check');
|
||||
var removeHDD = checkbox ? checkbox.checked : false;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Törlés folyamatban...';
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name, {
|
||||
method: 'DELETE',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({remove_hdd_data: removeHDD})
|
||||
});
|
||||
var data = await resp.json();
|
||||
if (data.ok) {
|
||||
var modal = document.getElementById('delete-modal');
|
||||
var removedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) {
|
||||
removedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Törölt adatok: ' + data.data.hdd_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
var preservedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) {
|
||||
preservedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Megőrzött adatok: ' + data.data.hdd_paths_preserved.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.hdd_note) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">' + data.data.hdd_note + '</p>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Sikeresen törölve!</h3>' +
|
||||
'<p style="color:var(--text-2)">Az alkalmazás (' + name + ') törölve lett.</p>' +
|
||||
removedInfo + preservedInfo +
|
||||
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Bezárás</button></div>';
|
||||
} else {
|
||||
showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba'));
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Törlés';
|
||||
}
|
||||
} catch (err) {
|
||||
showAlert('Hálózati hiba: ' + err.message);
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Törlés';
|
||||
}
|
||||
}
|
||||
async function removeStack(name) {
|
||||
var modal = document.createElement('div');
|
||||
modal.className = 'modal-overlay';
|
||||
modal.id = 'remove-modal';
|
||||
modal.innerHTML = '<div class="modal-card"><h3>Betöltés...</h3></div>';
|
||||
modal.addEventListener('click', function(e) { if (e.target === modal) closeRemoveModal(); });
|
||||
document.body.appendChild(modal);
|
||||
try {
|
||||
var [hddResp, backupResp] = await Promise.all([
|
||||
fetch('/api/stacks/' + name + '/hdd-data').then(function(r) { return r.json(); }),
|
||||
fetch('/api/stacks/' + name + '/backup-data').then(function(r) { return r.json(); })
|
||||
]);
|
||||
var sections = '';
|
||||
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Mindig törlődik:</strong>' +
|
||||
'<ul style="margin:.25rem 0;padding-left:1.2rem;color:var(--text-2);font-size:.85rem">' +
|
||||
'<li>Docker kötetek (adatbázis, alkalmazás konfiguráció)</li>' +
|
||||
'<li>Telepítési konfiguráció (app.yaml)</li>' +
|
||||
'<li>Másodlagos mentés ütemezése</li>' +
|
||||
'</ul></div>';
|
||||
|
||||
var hddCheckbox = '';
|
||||
if (hddResp.ok && hddResp.data && hddResp.data.has_hdd_data) {
|
||||
var hddPaths = '';
|
||||
hddResp.data.hdd_paths.forEach(function(p) {
|
||||
hddPaths += '<div class="modal-hdd-path">' + p.path + ' (' + (p.exists ? p.size_human : 'nem létezik') + ')</div>';
|
||||
});
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Felhasználói adatok a merevlemezen:</strong>' + hddPaths +
|
||||
'<label class="modal-checkbox"><input type="checkbox" id="remove-hdd-check"> Felhasználói adatok törlése</label>' +
|
||||
'<div class="alert alert-info" style="margin-top:.5rem;font-size:.8rem" id="remove-hdd-keep-warning">' +
|
||||
'Ha újratelepíti az alkalmazást, az adatokat újra importálnia kell, mivel az adatbázis törlődik. A megtartott adatok a továbbiakban NEM lesznek automatikusan mentve.' +
|
||||
'</div></div>';
|
||||
hddCheckbox = '\x3Cscript>document.getElementById("remove-hdd-check").addEventListener("change",function(){document.getElementById("remove-hdd-keep-warning").style.display=this.checked?"none":"";});<\/script>';
|
||||
}
|
||||
|
||||
var backupCheckbox = '';
|
||||
if (backupResp.ok && backupResp.data && backupResp.data.has_backups) {
|
||||
var bkPaths = '';
|
||||
backupResp.data.backup_paths.forEach(function(p) {
|
||||
if (p.exists) bkPaths += '<div class="modal-hdd-path">' + p.path + ' (' + p.size_human + ')</div>';
|
||||
});
|
||||
if (bkPaths) {
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Mentési adatok:</strong>' + bkPaths +
|
||||
'<label class="modal-checkbox"><input type="checkbox" id="remove-backup-check"> Mentési adatok törlése</label>' +
|
||||
'<div class="alert alert-info" style="margin-top:.5rem;font-size:.8rem">' +
|
||||
'Az éjszakai restic pillanatképek nem törölhetők egyenként — a megőrzési szabályok szerint automatikusan elavulnak.' +
|
||||
'</div></div>';
|
||||
}
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Alkalmazás eltávolítása: ' + name + '</h3>' +
|
||||
'<p style="color:var(--text-2);font-size:.9rem;margin-bottom:.75rem">Az alkalmazás visszaáll "Nincs telepítve" állapotba. A sablon megmarad, újratelepíthető.</p>' +
|
||||
'<div class="alert alert-warning" style="margin-bottom:.75rem">Ez a művelet nem visszavonható!</div>' +
|
||||
sections +
|
||||
'<div class="modal-actions">' +
|
||||
'<button class="btn btn-outline" onclick="closeRemoveModal()">Mégsem</button>' +
|
||||
'<button class="btn btn-danger" id="confirm-remove-btn" onclick="confirmRemoveStack(\'' + name + '\')">Eltávolítás</button>' +
|
||||
'</div>' + hddCheckbox;
|
||||
} catch (err) {
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Hiba</h3><p style="color:var(--text-2)">Nem sikerült lekérni az adatokat: ' + err.message + '</p>' +
|
||||
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeRemoveModal()">Bezárás</button></div>';
|
||||
}
|
||||
}
|
||||
function closeRemoveModal() {
|
||||
var modal = document.getElementById('remove-modal');
|
||||
if (modal) modal.remove();
|
||||
}
|
||||
async function confirmRemoveStack(name) {
|
||||
var btn = document.getElementById('confirm-remove-btn');
|
||||
var hddCheck = document.getElementById('remove-hdd-check');
|
||||
var backupCheck = document.getElementById('remove-backup-check');
|
||||
var removeHDD = hddCheck ? hddCheck.checked : false;
|
||||
var removeBackups = backupCheck ? backupCheck.checked : false;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Eltávolítás folyamatban...';
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name + '/remove', {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({remove_hdd_data: removeHDD, remove_backups: removeBackups})
|
||||
});
|
||||
var data = await resp.json();
|
||||
if (data.ok) {
|
||||
var modal = document.getElementById('remove-modal');
|
||||
var removedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) {
|
||||
removedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Törölt adatok: ' + data.data.hdd_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.backup_paths_removed && data.data.backup_paths_removed.length > 0) {
|
||||
removedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Törölt mentések: ' + data.data.backup_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
var preservedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) {
|
||||
preservedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Megőrzött adatok: ' + data.data.hdd_paths_preserved.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.hdd_note) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">' + data.data.hdd_note + '</p>';
|
||||
}
|
||||
if (data.data && data.data.backup_paths_refused && data.data.backup_paths_refused.length > 0) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Nem törölt mentések: ' + data.data.backup_paths_refused.join('; ') + '</p>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Sikeresen eltávolítva!</h3>' +
|
||||
'<p style="color:var(--text-2)">Az alkalmazás (' + name + ') eltávolítva. Újratelepíthető a Telepítés gombbal.</p>' +
|
||||
removedInfo + preservedInfo +
|
||||
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Bezárás</button></div>';
|
||||
} else {
|
||||
showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba'));
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Eltávolítás';
|
||||
}
|
||||
} catch (err) {
|
||||
showAlert('Hálózati hiba: ' + err.message);
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Eltávolítás';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,6 +1,25 @@
|
||||
20:54:08 ==== tandoor: ghcr.io/tandoorrecipes/recipes:2.6.13 -> ghcr.io/tandoorrecipes/recipes:2.6.15 (sub=recipes, class=db-postgres)
|
||||
20:54:08 [1] tandoor already deployed — reusing
|
||||
20:54:11 [2] seeding through the app's own front door
|
||||
20:54:13 tandoor: createsuperuser :: Traceback (most recent call last): File "/opt/recipes/manage.py", line 8, in <module> from django.core.management import execute_from_command_line ModuleNotFoun
|
||||
20:54:15 tandoor: the account did not appear in the app's own ORM :: aceback (most recent call last): File "/opt/recipes/manage.py", line 10, in <module> raise ImportError( ...<3 lines>... ) from exc ImportError: Couldn't import Django. Are you sure it's installed and
|
||||
20:54:15 [9] verdict inconclusive -> /mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/update-night-2026-09-21/apps/tandoor/verdict.json
|
||||
21:15:25 ==== tandoor: ghcr.io/tandoorrecipes/recipes:2.6.13 -> ghcr.io/tandoorrecipes/recipes:2.6.15 (sub=recipes, class=db-postgres)
|
||||
21:15:25 [1] tandoor already deployed — reusing
|
||||
21:15:27 [2] seeding through the app's own front door
|
||||
21:15:37 tandoor: createsuperuser :: Running django-vite in production mode (no HMR) Superuser created successfully.
|
||||
21:15:40 tandoor: seeded superuser drill316903
|
||||
21:15:40 [3] control C1 — reading the seed back BEFORE the update
|
||||
21:15:46 tandoor: readback of the seeded account found=True
|
||||
21:15:46 [4] „Mentés most" -> 200 {'ok': True, 'message': 'Mentés elindítva'}
|
||||
21:16:42 [4] backup idle; last=None
|
||||
21:16:43 [5] drill commit c7477d1fb921: tandoor ghcr.io/tandoorrecipes/recipes:2.6.13 -> ghcr.io/tandoorrecipes/recipes:2.6.15 (push rc=0)
|
||||
21:16:47 [5] badge HU: [{'title': 'Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.', 'text': 'Frissítés elérhető — ma'}]
|
||||
21:16:47 [5] badge EN: [{'title': 'A newer version of this app is available. Select the Update button to start it.', 'text': 'Update available — today'}]
|
||||
21:16:47 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}
|
||||
21:16:47 + 0.0s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None
|
||||
21:16:48 + 1.0s phase=pulling label=Új verzió letöltése… err=None hold=None
|
||||
21:17:42 + 54.3s phase=starting label=Indítás az új verzióval… err=None hold=None
|
||||
21:17:46 + 58.4s phase=verifying label=Működés ellenőrzése… err=None hold=None
|
||||
21:22:49 + 361.9s phase=failed label=A frissítés nem sikerült err=A(z) tandoor frissítése 2026-09-21 21:22-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:15 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza. hold=A(z) tandoor frissítése 2026-09-21 21:22-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:15 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.
|
||||
21:22:49 [7] reading the seed back AFTER the update
|
||||
21:22:52 tandoor: READBACK UNUSABLE — a username that cannot exist did not read as absent (None) :: Error response from daemon: No such container: tandoor
|
||||
21:22:54 [8] pinned = {'tandoor': 'ghcr.io/tandoorrecipes/recipes:2.6.15', 'tandoor-postgres': 'postgres:16-alpine'}
|
||||
21:22:54 [8] installed = {'tandoor': 'ghcr.io/tandoorrecipes/recipes:2.6.13', 'tandoor-postgres': 'postgres:16-alpine'}
|
||||
21:22:54 [8] compose = ['image: ghcr.io/tandoorrecipes/recipes:2.6.15', 'image: postgres:16-alpine']
|
||||
21:22:54 [8] inspect = []
|
||||
21:22:54 [9] verdict failed -> /mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/update-night-2026-09-21/apps/tandoor/verdict.json
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"before": {
|
||||
"pinned_images": {
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.13",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"installed_images": {
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.13",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"catalog_images": {
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.13",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: ghcr.io/tandoorrecipes/recipes:2.6.13",
|
||||
"image: postgres:16-alpine"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"tandoor ghcr.io/tandoorrecipes/recipes:2.6.13 running=true restarts=0",
|
||||
"tandoor-postgres postgres:16-alpine running=true restarts=0"
|
||||
]
|
||||
},
|
||||
"after": {
|
||||
"pinned_images": {
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.15",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"installed_images": {
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.13",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"catalog_images": {
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.15",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: ghcr.io/tandoorrecipes/recipes:2.6.15",
|
||||
"image: postgres:16-alpine"
|
||||
],
|
||||
"docker_inspect": []
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"accepted": true,
|
||||
"http": "202",
|
||||
"phases": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"phase": "safety-dump",
|
||||
"label": "Adatbázis pillanatkép…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 1.0,
|
||||
"phase": "pulling",
|
||||
"label": "Új verzió letöltése…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 54.3,
|
||||
"phase": "starting",
|
||||
"label": "Indítás az új verzióval…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 58.4,
|
||||
"phase": "verifying",
|
||||
"label": "Működés ellenőrzése…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 361.9,
|
||||
"phase": "failed",
|
||||
"label": "A frissítés nem sikerült",
|
||||
"updating": false,
|
||||
"error": "A(z) tandoor frissítése 2026-09-21 21:22-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:15 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"hold": "A(z) tandoor frissítése 2026-09-21 21:22-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:15 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza."
|
||||
}
|
||||
],
|
||||
"duration_s": 361.9,
|
||||
"final_phase": "failed",
|
||||
"update_error": "A(z) tandoor frissítése 2026-09-21 21:22-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:15 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"hold_reason": "A(z) tandoor frissítése 2026-09-21 21:22-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:15 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"state": "stopped"
|
||||
}
|
||||
@@ -7,19 +7,43 @@
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.13",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"to": {},
|
||||
"verdict": "inconclusive",
|
||||
"seed_read_before": false,
|
||||
"to": {
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.15",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"verdict": "failed",
|
||||
"seed_read_before": true,
|
||||
"seed_read_after": false,
|
||||
"healthy_after": false,
|
||||
"migration_observed": null,
|
||||
"abort": "not-attempted",
|
||||
"abort_detail": null,
|
||||
"duration_s": 6.6,
|
||||
"measured_at": "2026-09-21T18:54:08.826237+00:00",
|
||||
"duration_s": 361.9,
|
||||
"measured_at": "2026-09-21T19:15:25.308811+00:00",
|
||||
"evidence": "apps/tandoor/",
|
||||
"notes": [
|
||||
"seed refused through the app's own route — see log.txt for what was tried",
|
||||
"seed route did not work tonight"
|
||||
]
|
||||
"the edge ended HELD or failed — this is a RESULT, not an error of the run"
|
||||
],
|
||||
"observables_after": {
|
||||
"pinned_images": {
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.15",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"installed_images": {
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.13",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"catalog_images": {
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.15",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: ghcr.io/tandoorrecipes/recipes:2.6.15",
|
||||
"image: postgres:16-alpine"
|
||||
],
|
||||
"docker_inspect": []
|
||||
},
|
||||
"final_phase": "failed",
|
||||
"hold_reason": "A(z) tandoor frissítése 2026-09-21 21:22-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:15 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"update_error": "A(z) tandoor frissítése 2026-09-21 21:22-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:15 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza."
|
||||
}
|
||||
@@ -18,7 +18,6 @@
|
||||
"measured_at": "2026-09-21T18:37:30.611425+00:00",
|
||||
"evidence": "apps/vaultwarden/",
|
||||
"notes": [
|
||||
"seed refused through the app's own route — see log.txt for what was tried",
|
||||
"seed route did not work tonight"
|
||||
"INCONCLUSIVE BY DESIGN, not by a gap in the harness: the catalog CLOSES self-registration on purpose (`SIGNUPS_ALLOWED=false`, R-512 — *a stranger who guesses vault.<domain> must not be able to register*), so `/api/accounts/register` answers 404 and there is NO account-creating route without the admin secret. Vaultwarden also ships no CLI. Tried: `POST /api/accounts/register` with a valid KDF envelope. This app cannot be seeded headlessly while that setting stands, and the setting is right."
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
vikunja | time=2026-09-21T21:26:24.321+02:00 level=INFO msg="No config file found, using default or config from environment variables."
|
||||
vikunja | time=2026-09-21T21:26:24.322+02:00 level=WARN msg="config: service.jwtsecret is deprecated and will be removed in a future release. Please use service.secret instead."
|
||||
vikunja | time=2026-09-21T21:26:24.322+02:00 level=INFO msg="Running migrations…"
|
||||
vikunja | time=2026-09-21T21:26:24.322+02:00 level=INFO msg="Using SQLite database at: /db/vikunja.db"
|
||||
vikunja | time=2026-09-21T21:26:24.356+02:00 level=INFO msg="Ran all migrations successfully."
|
||||
vikunja | time=2026-09-21T21:26:24.356+02:00 level=WARN msg="No license key configured. Pro features have been disabled."
|
||||
vikunja | time=2026-09-21T21:26:24.361+02:00 level=INFO msg="Mailer is disabled, not sending reminders per mail"
|
||||
vikunja | time=2026-09-21T21:26:24.361+02:00 level=INFO msg="Mailer is disabled, not sending overdue reminders per mail"
|
||||
vikunja | time=2026-09-21T21:26:24.361+02:00 level=INFO msg="Vikunja version v2.6.0"
|
||||
vikunja | time=2026-09-21T21:26:24.361+02:00 level=INFO msg="CORS enabled with origins: http://127.0.0.1:*, http://localhost:*, https://tasks.enkisfelhom.hu"
|
||||
vikunja | time=2026-09-21T21:26:24.389+02:00 level=INFO msg="HTTP server listening on :3456"
|
||||
vikunja | time=2026-09-21T21:26:43.306+02:00 level=INFO component=http remote_ip=172.18.0.7 method=GET uri=/api/v1/info status=200 latency=506.135µs user_agent=Go-http-client/1.1
|
||||
vikunja | time=2026-09-21T21:26:44.453+02:00 level=INFO component=http remote_ip=172.18.0.7 method=GET uri=/api/v1/info status=200 latency=241.036µs user_agent=Go-http-client/1.1
|
||||
vikunja | time=2026-09-21T21:26:44.840+02:00 level=INFO component=http remote_ip=172.18.0.8 method=GET uri=/api/v1/info status=200 latency=231.067µs user_agent=curl/8.14.1
|
||||
vikunja | time=2026-09-21T21:26:44.998+02:00 level=ERROR component=http remote_ip=172.18.0.8 method=POST uri=/api/v1/login status=403 latency=134.42926ms user_agent=curl/8.14.1 err="Wrong username or password"
|
||||
vikunja | time=2026-09-21T21:26:45.160+02:00 level=INFO component=http remote_ip=172.18.0.8 method=POST uri=/api/v1/login status=200 latency=138.351197ms user_agent=curl/8.14.1
|
||||
vikunja | time=2026-09-21T21:26:45.188+02:00 level=INFO component=http remote_ip=172.18.0.8 method=GET uri=/api/v1/projects/3 status=200 latency=3.350797ms user_agent=curl/8.14.1
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"before": {
|
||||
"hu": [
|
||||
{
|
||||
"title": "Ez az alkalmazás a legfrissebb elérhető változatot futtatja.",
|
||||
"text": "Naprakész"
|
||||
}
|
||||
],
|
||||
"en": [
|
||||
{
|
||||
"title": "This app is running the newest version available.",
|
||||
"text": "Up to date"
|
||||
}
|
||||
]
|
||||
},
|
||||
"after": {
|
||||
"hu": [
|
||||
{
|
||||
"title": "Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.",
|
||||
"text": "Frissítés elérhető — ma"
|
||||
}
|
||||
],
|
||||
"en": [
|
||||
{
|
||||
"title": "A newer version of this app is available. Select the Update button to start it.",
|
||||
"text": "Update available — today"
|
||||
}
|
||||
]
|
||||
},
|
||||
"drill_commit": "98eda8839128"
|
||||
}
|
||||
@@ -1,9 +1,25 @@
|
||||
21:05:20 ==== vikunja: vikunja/vikunja:2.3.0 -> vikunja/vikunja:2.6.0 (sub=tasks, class=other)
|
||||
21:05:21 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
|
||||
21:05:26 [1] deployed, controller state=running, pinned={'vikunja': 'vikunja/vikunja:2.3.0'}
|
||||
21:05:28 [2] seeding through the app's own front door
|
||||
21:05:28 vikunja: register http=200
|
||||
21:05:28 vikunja: create project http=405
|
||||
21:05:28 vikunja: project refused {"message":"Method Not Allowed"}
|
||||
|
||||
21:05:28 [9] verdict inconclusive -> /mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/update-night-2026-09-21/apps/vikunja/verdict.json
|
||||
21:25:26 ==== vikunja: vikunja/vikunja:2.3.0 -> vikunja/vikunja:2.6.0 (sub=tasks, class=other)
|
||||
21:25:26 [1] vikunja already deployed — reusing
|
||||
21:25:28 [2] seeding through the app's own front door
|
||||
21:25:28 vikunja: register http=200
|
||||
21:25:28 vikunja: create project http=201
|
||||
21:25:28 [3] control C1 — reading the seed back BEFORE the update
|
||||
21:25:29 vikunja: readback of the seeded project http=200 ok=True
|
||||
21:25:29 [4] „Mentés most" -> 200 {'ok': True, 'message': 'Mentés elindítva'}
|
||||
21:26:14 [4] backup idle; last=None
|
||||
21:26:15 [5] drill commit 98eda8839128: vikunja vikunja/vikunja:2.3.0 -> vikunja/vikunja:2.6.0 (push rc=0)
|
||||
21:26:19 [5] badge HU: [{'title': 'Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.', 'text': 'Frissítés elérhető — ma'}]
|
||||
21:26:19 [5] badge EN: [{'title': 'A newer version of this app is available. Select the Update button to start it.', 'text': 'Update available — today'}]
|
||||
21:26:20 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}
|
||||
21:26:20 + 0.0s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None
|
||||
21:26:21 + 1.0s phase=pulling label=Új verzió letöltése… err=None hold=None
|
||||
21:26:24 + 4.1s phase=starting label=Indítás az új verzióval… err=None hold=None
|
||||
21:26:25 + 5.2s phase=verifying label=Működés ellenőrzése… err=None hold=None
|
||||
21:26:44 + 24.6s phase=done label=Frissítve err=None hold=None
|
||||
21:26:44 [7] reading the seed back AFTER the update
|
||||
21:26:45 vikunja: readback of the seeded project http=200 ok=True
|
||||
21:26:47 [8] pinned = {'vikunja': 'vikunja/vikunja:2.6.0'}
|
||||
21:26:47 [8] installed = {'vikunja': 'vikunja/vikunja:2.6.0'}
|
||||
21:26:47 [8] compose = ['image: vikunja/vikunja:2.6.0']
|
||||
21:26:47 [8] inspect = ['vikunja vikunja/vikunja:2.6.0 running=true restarts=0']
|
||||
21:26:47 [9] verdict proven -> /mnt/5_hdd/felhom.eu/git/felhom.eu/documentation/audits/update-night-2026-09-21/apps/vikunja/verdict.json
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
"installed_images": {
|
||||
"vikunja": "vikunja/vikunja:2.3.0"
|
||||
},
|
||||
"catalog_images": null,
|
||||
"catalog_images": {
|
||||
"vikunja": "vikunja/vikunja:2.3.0"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: vikunja/vikunja:2.3.0"
|
||||
],
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"before": {
|
||||
"pinned_images": {
|
||||
"vikunja": "vikunja/vikunja:2.3.0"
|
||||
},
|
||||
"installed_images": {
|
||||
"vikunja": "vikunja/vikunja:2.3.0"
|
||||
},
|
||||
"catalog_images": {
|
||||
"vikunja": "vikunja/vikunja:2.3.0"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: vikunja/vikunja:2.3.0"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"vikunja vikunja/vikunja:2.3.0 running=true restarts=0"
|
||||
]
|
||||
},
|
||||
"after": {
|
||||
"pinned_images": {
|
||||
"vikunja": "vikunja/vikunja:2.6.0"
|
||||
},
|
||||
"installed_images": {
|
||||
"vikunja": "vikunja/vikunja:2.6.0"
|
||||
},
|
||||
"catalog_images": {
|
||||
"vikunja": "vikunja/vikunja:2.6.0"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: vikunja/vikunja:2.6.0"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"vikunja vikunja/vikunja:2.6.0 running=true restarts=0"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"accepted": true,
|
||||
"http": "202",
|
||||
"phases": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"phase": "safety-dump",
|
||||
"label": "Adatbázis pillanatkép…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 1.0,
|
||||
"phase": "pulling",
|
||||
"label": "Új verzió letöltése…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 4.1,
|
||||
"phase": "starting",
|
||||
"label": "Indítás az új verzióval…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 5.2,
|
||||
"phase": "verifying",
|
||||
"label": "Működés ellenőrzése…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 24.6,
|
||||
"phase": "done",
|
||||
"label": "Frissítve",
|
||||
"updating": false,
|
||||
"error": null,
|
||||
"hold": null
|
||||
}
|
||||
],
|
||||
"duration_s": 24.6,
|
||||
"final_phase": "done",
|
||||
"update_error": null,
|
||||
"hold_reason": null,
|
||||
"state": "running"
|
||||
}
|
||||
@@ -6,19 +6,38 @@
|
||||
"from": {
|
||||
"vikunja": "vikunja/vikunja:2.3.0"
|
||||
},
|
||||
"to": {},
|
||||
"verdict": "inconclusive",
|
||||
"seed_read_before": false,
|
||||
"seed_read_after": false,
|
||||
"healthy_after": false,
|
||||
"migration_observed": null,
|
||||
"to": {
|
||||
"vikunja": "vikunja/vikunja:2.6.0"
|
||||
},
|
||||
"verdict": "proven",
|
||||
"seed_read_before": true,
|
||||
"seed_read_after": true,
|
||||
"healthy_after": true,
|
||||
"migration_observed": "vikunja | time=2026-09-21T21:26:24.322+02:00 level=INFO msg=\"Running migrations…\"",
|
||||
"abort": "not-attempted",
|
||||
"abort_detail": null,
|
||||
"duration_s": 7.9,
|
||||
"measured_at": "2026-09-21T19:05:21.114505+00:00",
|
||||
"duration_s": 24.6,
|
||||
"measured_at": "2026-09-21T19:25:26.344387+00:00",
|
||||
"evidence": "apps/vikunja/",
|
||||
"notes": [
|
||||
"seed refused through the app's own route — see log.txt for what was tried",
|
||||
"seed route did not work tonight"
|
||||
]
|
||||
"notes": [],
|
||||
"observables_after": {
|
||||
"pinned_images": {
|
||||
"vikunja": "vikunja/vikunja:2.6.0"
|
||||
},
|
||||
"installed_images": {
|
||||
"vikunja": "vikunja/vikunja:2.6.0"
|
||||
},
|
||||
"catalog_images": {
|
||||
"vikunja": "vikunja/vikunja:2.6.0"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: vikunja/vikunja:2.6.0"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"vikunja vikunja/vikunja:2.6.0 running=true restarts=0"
|
||||
]
|
||||
},
|
||||
"final_phase": "done",
|
||||
"hold_reason": null,
|
||||
"update_error": null
|
||||
}
|
||||
@@ -19,7 +19,6 @@
|
||||
"measured_at": "2026-09-21T18:54:15.502005+00:00",
|
||||
"evidence": "apps/zipline/",
|
||||
"notes": [
|
||||
"seed refused through the app's own route — see log.txt for what was tried",
|
||||
"seed route did not work tonight"
|
||||
"INCONCLUSIVE BY DESIGN: the deploy answers `E1037: User registration is disabled`, so no first account can be created from outside. Tried: `POST /api/auth/register` and `POST /api/auth/setup`. SEPARATELY, zipline is one of R-618's two confirmed victims — its `.felhom.yml` probe expects 200 on `/api/health`, which the app answers 404 — so even with a seed its update would have been HELD by a wrong probe rather than by anything about the edge."
|
||||
]
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
[
|
||||
[
|
||||
"adventurelog",
|
||||
{
|
||||
"adventurelog": "ghcr.io/seanmorley15/adventurelog-backend:v0.12.1",
|
||||
"adventurelog-frontend": "ghcr.io/seanmorley15/adventurelog-frontend:v0.12.1",
|
||||
"adventurelog-postgres": "postgis/postgis:16-3.5-alpine"
|
||||
},
|
||||
{
|
||||
"adventurelog": "ghcr.io/seanmorley15/adventurelog-backend:v0.13.0",
|
||||
"adventurelog-frontend": "ghcr.io/seanmorley15/adventurelog-frontend:v0.13.0",
|
||||
"adventurelog-postgres": "postgis/postgis:16-3.5-alpine"
|
||||
}
|
||||
],
|
||||
[
|
||||
"docmost",
|
||||
{
|
||||
"docmost": "docmost/docmost:0.96.0",
|
||||
"docmost-postgres": "postgres:16-alpine",
|
||||
"docmost-redis": "redis:7-alpine"
|
||||
},
|
||||
{
|
||||
"docmost": "docmost/docmost:0.96.0",
|
||||
"docmost-postgres": "postgres:17-alpine",
|
||||
"docmost-redis": "redis:7-alpine"
|
||||
}
|
||||
],
|
||||
[
|
||||
"glance",
|
||||
{
|
||||
"glance": "glanceapp/glance:v0.8.6"
|
||||
},
|
||||
{
|
||||
"glance": "glanceapp/glance:v0.8.5"
|
||||
}
|
||||
],
|
||||
[
|
||||
"tandoor",
|
||||
{
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.13",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
},
|
||||
{
|
||||
"tandoor": "ghcr.io/tandoorrecipes/recipes:2.6.15",
|
||||
"tandoor-postgres": "postgres:16-alpine"
|
||||
}
|
||||
],
|
||||
[
|
||||
"uptime-kuma",
|
||||
{
|
||||
"uptime-kuma": "louislam/uptime-kuma:2.5.1"
|
||||
},
|
||||
{
|
||||
"uptime-kuma": "louislam/uptime-kuma:2.5.5"
|
||||
}
|
||||
],
|
||||
[
|
||||
"wishlist",
|
||||
{
|
||||
"wishlist": "ghcr.io/cmintey/wishlist:v0.67.0"
|
||||
},
|
||||
{
|
||||
"wishlist": "ghcr.io/cmintey/wishlist:v0.66.0"
|
||||
}
|
||||
]
|
||||
]
|
||||
@@ -0,0 +1,7 @@
|
||||
[
|
||||
"adventurelog",
|
||||
"docmost",
|
||||
"tandoor",
|
||||
"uptime-kuma",
|
||||
"wishlist"
|
||||
]
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
image: localhost:5000/drill/glance:1.0.0
|
||||
{
|
||||
"pinned_images": {
|
||||
"glance": "localhost:5000/drill/glance:1.0.0"
|
||||
},
|
||||
"installed_images": {
|
||||
"glance": "localhost:5000/drill/glance:1.0.0"
|
||||
},
|
||||
"catalog_images": null,
|
||||
"live_compose_image_lines": [
|
||||
"image: localhost:5000/drill/glance:1.0.0"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"glance localhost:5000/drill/glance:1.0.0 running=true restarts=0"
|
||||
]
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"drill_commit": "c644164ee439",
|
||||
"badges": {
|
||||
"hu": [
|
||||
{
|
||||
"title": "Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.",
|
||||
"text": "Frissítés elérhető — ma"
|
||||
}
|
||||
],
|
||||
"en": [
|
||||
{
|
||||
"title": "A newer version of this app is available. Select the Update button to start it.",
|
||||
"text": "Update available — today"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
2026-09-21T19:47:49Z === pass 1/3
|
||||
2026-09-21T19:47:49Z glance BEHIND and within a major — pressing Update
|
||||
2026-09-21T19:47:49Z glance: phase=safety-dump updating=True
|
||||
2026-09-21T19:47:51Z glance: phase=verifying updating=True
|
||||
2026-09-21T19:53:02Z glance: phase=failed updating=False
|
||||
2026-09-21T19:53:02Z glance ENDED held after 312.9s
|
||||
2026-09-21T19:54:32Z === pass 2/3
|
||||
2026-09-21T19:56:03Z === pass 3/3
|
||||
2026-09-21T19:56:03Z === summary outcomes={'glance': ('held', 312.9)} never_again=['glance']
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"state": "stopped",
|
||||
"updating": false,
|
||||
"update_phase": "failed",
|
||||
"update_phase_label": "A frissítés nem sikerült",
|
||||
"update_error": "A(z) glance frissítése 2026-09-21 21:53-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:47 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"hold_reason": "A(z) glance frissítése 2026-09-21 21:53-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:47 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.",
|
||||
"observables": {
|
||||
"pinned_images": {
|
||||
"glance": "localhost:5000/drill/glance:1.0.1"
|
||||
},
|
||||
"installed_images": {
|
||||
"glance": "localhost:5000/drill/glance:1.0.0"
|
||||
},
|
||||
"catalog_images": {
|
||||
"glance": "localhost:5000/drill/glance:1.0.1"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: localhost:5000/drill/glance:1.0.1"
|
||||
],
|
||||
"docker_inspect": []
|
||||
}
|
||||
}
|
||||
+664
@@ -0,0 +1,664 @@
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" class="no-js">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Glance — Felhom.eu</title>
|
||||
|
||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg?v=0.261.0">
|
||||
<link rel="stylesheet" href="/static/style.css?v=0.261.0">
|
||||
<meta name="csrf-token" content="fab572e2f6d427f4d84e1174a6a9ad4d6c7730fc751a7ab6c826734b03ecf8c2">
|
||||
<script>
|
||||
|
||||
|
||||
document.documentElement.className=document.documentElement.className.replace('no-js','js');
|
||||
function csrfHeaders(){var el=document.querySelector('meta[name="csrf-token"]');return el?{'X-CSRF-Token':el.content}:{};}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function felhomConfirm(el,question,onYes){
|
||||
if(!el||el.dataset.fcOpen)return;
|
||||
el.dataset.fcOpen='1';
|
||||
var wrap=document.createElement('span');wrap.className='inline-confirm';
|
||||
var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question;
|
||||
var yes=document.createElement('button');yes.type='button';yes.className='btn btn-xs btn-danger';yes.textContent='Yes';
|
||||
var no=document.createElement('button');no.type='button';no.className='btn btn-xs btn-outline';no.textContent='Cancel';
|
||||
wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no);
|
||||
el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling);
|
||||
function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;}
|
||||
no.addEventListener('click',close);
|
||||
yes.addEventListener('click',function(){close();onYes();});
|
||||
}
|
||||
document.addEventListener('click',function(e){
|
||||
var btn=e.target.closest?e.target.closest('[data-confirm]'):null;
|
||||
if(!btn)return;
|
||||
e.preventDefault();
|
||||
felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){
|
||||
var form=btn.closest('form');
|
||||
if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
document.addEventListener('click',function(e){
|
||||
var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null;
|
||||
if(!btn)return;
|
||||
var group=btn.closest('.nav-group');
|
||||
if(!group)return;
|
||||
var willOpen=!group.classList.contains('is-open');
|
||||
document.querySelectorAll('.nav-group.is-open').forEach(function(g){
|
||||
g.classList.remove('is-open');
|
||||
var t=g.querySelector('.nav-group-toggle');
|
||||
if(t)t.setAttribute('aria-expanded','false');
|
||||
});
|
||||
if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');}
|
||||
});
|
||||
function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML='<h3>Message</h3><pre style="white-space:pre-wrap;word-break:break-word;background:var(--bg-2);padding:.75rem;border-radius:.375rem;font-size:.85rem;max-height:60vh;overflow-y:auto;user-select:text;cursor:text">'+msg.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')+'</pre><div class="modal-actions"><button class="btn btn-primary" onclick="document.getElementById(\'alert-modal\').remove()">OK</button></div>';o.appendChild(c);document.body.appendChild(o);}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display:none" aria-hidden="true">
|
||||
<symbol id="i-hard-drive" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 16h.01" /> <path d="M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" /> <path d="M21.946 12.013H2.054" /> <path d="M6 16h.01" /></symbol>
|
||||
<symbol id="i-cpu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20v2" /> <path d="M12 2v2" /> <path d="M17 20v2" /> <path d="M17 2v2" /> <path d="M2 12h2" /> <path d="M2 17h2" /> <path d="M2 7h2" /> <path d="M20 12h2" /> <path d="M20 17h2" /> <path d="M20 7h2" /> <path d="M7 20v2" /> <path d="M7 2v2" /> <rect x="4" y="4" width="16" height="16" rx="2" /> <rect x="8" y="8" width="8" height="8" rx="1" /></symbol>
|
||||
<symbol id="i-memory-stick" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 12v-2" /> <path d="M12 18v-2" /> <path d="M16 12v-2" /> <path d="M16 18v-2" /> <path d="M2 11h1.5" /> <path d="M20 18v-2" /> <path d="M20.5 11H22" /> <path d="M4 18v-2" /> <path d="M8 12v-2" /> <path d="M8 18v-2" /> <rect x="2" y="6" width="20" height="10" rx="2" /></symbol>
|
||||
<symbol id="i-thermometer" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 4v10.54a4 4 0 1 1-4 0V4a2 2 0 0 1 4 0Z" /></symbol>
|
||||
<symbol id="i-triangle-alert" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" /> <path d="M12 9v4" /> <path d="M12 17h.01" /></symbol>
|
||||
<symbol id="i-lock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="11" x="3" y="11" rx="2" ry="2" /> <path d="M7 11V7a5 5 0 0 1 10 0v4" /></symbol>
|
||||
<symbol id="i-cloud" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z" /></symbol>
|
||||
<symbol id="i-square" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" /></symbol>
|
||||
<symbol id="i-rotate-cw" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" /> <path d="M21 3v5h-5" /></symbol>
|
||||
<symbol id="i-file-text" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /> <path d="M14 2v5a1 1 0 0 0 1 1h5" /> <path d="M10 9H8" /> <path d="M16 13H8" /> <path d="M16 17H8" /></symbol>
|
||||
<symbol id="i-external-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6" /> <path d="M10 14 21 3" /> <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></symbol>
|
||||
<symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /></symbol>
|
||||
<symbol id="i-server" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="8" x="2" y="2" rx="2" ry="2" /> <rect width="20" height="8" x="2" y="14" rx="2" ry="2" /> <line x1="6" x2="6.01" y1="6" y2="6" /> <line x1="6" x2="6.01" y1="18" y2="18" /></symbol>
|
||||
<symbol id="i-share" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3" /> <circle cx="6" cy="12" r="3" /> <circle cx="18" cy="19" r="3" /> <line x1="8.59" x2="15.42" y1="13.51" y2="17.49" /> <line x1="15.41" x2="8.59" y1="6.51" y2="10.49" /></symbol>
|
||||
<symbol id="i-layout-grid" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="7" x="3" y="3" rx="1" /> <rect width="7" height="7" x="14" y="3" rx="1" /> <rect width="7" height="7" x="14" y="14" rx="1" /> <rect width="7" height="7" x="3" y="14" rx="1" /></symbol>
|
||||
<symbol id="i-rocket" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91 0z" /> <path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z" /> <path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0" /> <path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5" /></symbol>
|
||||
<symbol id="i-settings" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /> <circle cx="12" cy="12" r="3" /></symbol>
|
||||
<symbol id="i-bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.268 21a2 2 0 0 0 3.464 0" /> <path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" /></symbol>
|
||||
<symbol id="i-x" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18" /> <path d="m6 6 12 12" /></symbol>
|
||||
<symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5" /></symbol>
|
||||
<symbol id="i-pencil" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" /> <path d="m15 5 4 4" /></symbol>
|
||||
<symbol id="i-info" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /> <path d="M12 16v-4" /> <path d="M12 8h.01" /></symbol>
|
||||
<symbol id="i-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 15V3" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /> <path d="m7 10 5 5 5-5" /></symbol>
|
||||
<symbol id="i-upload" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v12" /> <path d="m17 8-5-5-5 5" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /></symbol>
|
||||
<symbol id="i-trash-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 11v6" /> <path d="M14 11v6" /> <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /> <path d="M3 6h18" /> <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></symbol>
|
||||
<symbol id="i-wrench" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z" /></symbol>
|
||||
<symbol id="i-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" /> <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" /></symbol>
|
||||
<symbol id="i-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34" /> <circle cx="11" cy="11" r="8" /></symbol>
|
||||
<symbol id="i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14" /> <path d="M12 5v14" /></symbol>
|
||||
<symbol id="i-chevron-down" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6" /></symbol>
|
||||
<symbol id="i-play" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z" /></symbol>
|
||||
<symbol id="i-pause" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="14" y="3" width="5" height="18" rx="1" /> <rect x="5" y="3" width="5" height="18" rx="1" /></symbol>
|
||||
<symbol id="i-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h16" /> <path d="M4 6h16" /> <path d="M4 18h16" /></symbol>
|
||||
</svg>
|
||||
<header class="mobile-topbar">
|
||||
<a href="/" class="mobile-topbar-logo"><img src="/static/felhom-logo.svg?v=0.261.0" alt="Felhom.eu"></a>
|
||||
<button type="button" class="nav-burger" aria-expanded="false" aria-controls="sidebar" aria-label="Menu">
|
||||
<svg class="ico"><use href="#i-menu"/></svg>
|
||||
</button>
|
||||
</header>
|
||||
<div class="nav-backdrop" hidden></div>
|
||||
<nav class="sidebar" id="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<img src="/static/felhom-logo.svg?v=0.261.0" alt="Felhom.eu" class="sidebar-logo">
|
||||
</div>
|
||||
<ul class="nav-links">
|
||||
<li><a href="/launcher" class=""><svg class="ico"><use href="#i-rocket"/></svg>Launcher</a></li>
|
||||
<li><a href="/dashboard" class=""><svg class="ico"><use href="#i-layout-grid"/></svg>Dashboard</a></li>
|
||||
<li><a href="/stacks" class="active"><svg class="ico"><use href="#i-cloud"/></svg>Apps</a></li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-storage"><svg class="ico"><use href="#i-hard-drive"/></svg>Storage<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-storage" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/storage" class="">Drives</a></li>
|
||||
<li><a href="/storage/network" class="">Network storage</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-backups"><svg class="ico"><use href="#i-shield"/></svg>Backup<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-backups" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/backups" class="">Overview</a></li>
|
||||
<li><a href="/backups/remote" class="">Remote backup</a></li>
|
||||
<li><a href="/backups/apps" class="">Apps</a></li>
|
||||
<li><a href="/backups/restore" class="">Restore</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-sharing"><svg class="ico"><use href="#i-share"/></svg>Sharing<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-sharing" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/sharing" class="">Network sharing</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/monitoring" class=""><svg class="ico"><use href="#i-cpu"/></svg>System monitor</a></li>
|
||||
<li><a href="/debug" class=""><svg class="ico"><use href="#i-wrench"/></svg>Debug</a></li>
|
||||
</ul>
|
||||
<div class="sidebar-bottom">
|
||||
<div class="nav-group-label">Settings</div>
|
||||
<ul class="nav-links nav-links-sub">
|
||||
<li><a href="/settings" class=""><svg class="ico"><use href="#i-settings"/></svg>System</a></li>
|
||||
<li><a href="/settings/notifications" class=""><svg class="ico"><use href="#i-bell"/></svg>Notifications</a></li>
|
||||
<li><a href="/settings/security" class=""><svg class="ico"><use href="#i-lock"/></svg>Security and access</a></li>
|
||||
</ul>
|
||||
<div class="sidebar-footer">
|
||||
<span class="version">0.261.0</span><details class="lang-globe">
|
||||
<summary class="lang-globe-btn" aria-label="Language" title="Language"><svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10" /><path d="M2 12h20" /><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" /></svg></summary>
|
||||
<ul class="lang-globe-menu">
|
||||
<li><form method="POST" action="/settings/language"><input type="hidden" name="_csrf" value="fab572e2f6d427f4d84e1174a6a9ad4d6c7730fc751a7ab6c826734b03ecf8c2"><input type="hidden" name="back" value="/apps/glance"><button type="submit" name="lang" value="hu" lang="hu" class="lang-globe-item">Magyar</button></form></li>
|
||||
<li><form method="POST" action="/settings/language"><input type="hidden" name="_csrf" value="fab572e2f6d427f4d84e1174a6a9ad4d6c7730fc751a7ab6c826734b03ecf8c2"><input type="hidden" name="back" value="/apps/glance"><button type="submit" name="lang" value="en" lang="en" class="lang-globe-item current" aria-current="true">English</button></form></li>
|
||||
</ul>
|
||||
</details>
|
||||
<a href="/logout" class="logout-link">Sign out ↗</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<main class="content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="alerts-container">
|
||||
|
||||
|
||||
<div class="alert-banner alert-banner-warning">
|
||||
<span class="alert-icon"><svg class="ico"><use href="#i-triangle-alert"/></svg></span>
|
||||
<span class="alert-message">An installed app is not running: Glance (stopped)</span>
|
||||
<a href="/monitoring" class="alert-link">System monitor →</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="alert-banner alert-banner-warning">
|
||||
<span class="alert-icon"><svg class="ico"><use href="#i-triangle-alert"/></svg></span>
|
||||
<span class="alert-message">The hub connection is off — central monitoring is not running</span>
|
||||
<a href="/monitoring" class="alert-link">System monitor →</a>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="page-header">
|
||||
<div style="display:flex;align-items:center;gap:1rem">
|
||||
<a href="/stacks" class="btn btn-sm btn-outline">← Apps</a>
|
||||
<h2>Glance</h2>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:.5rem">
|
||||
|
||||
<span class="stack-state-badge state-neutral">Stopped</span>
|
||||
|
||||
|
||||
<span class="tag tag-warn" title="A newer version of this app is available. Select the Update button to start it.">Update available — today</span>
|
||||
<a href="https://dashboard.enkisfelhom.hu" target="_blank" class="btn btn-sm btn-outline">Open ↗</a>
|
||||
<a href="/stacks/glance/logs" class="btn btn-sm btn-outline">Log</a>
|
||||
|
||||
<a href="/stacks/glance/export" class="btn btn-sm btn-outline">Export</a>
|
||||
<a href="/stacks/glance/deploy" class="btn btn-sm btn-outline">Settings</a>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="alert alert-error" style="margin-top:1rem" data-held="true">A(z) glance frissítése 2026-09-21 21:53-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:47 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza. <a href="/backups/apps" class="btn btn-sm btn-outline">Backups</a></div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-hero">
|
||||
<img class="app-info-logo" src="/static/assets/glance-logo.svg"
|
||||
alt="Glance" data-fallback="/static/app-placeholder.svg"
|
||||
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/glance-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
||||
<div class="app-info-hero-text">
|
||||
|
||||
<p class="app-info-tagline">A minimalist dashboard - RSS, weather and bookmarks on one page</p>
|
||||
|
||||
<div class="stack-meta-badges">
|
||||
<span class="meta-badge">~20M RAM</span>
|
||||
<span class="meta-badge">dashboard</span>
|
||||
|
||||
<span class="meta-badge meta-badge-ok">Runs on Pi</span>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="app-screenshots" id="screenshots">
|
||||
<img src="/static/assets/glance-screenshot-1.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
<img src="/static/assets/glance-screenshot-2.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
<img src="/static/assets/glance-screenshot-3.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card" style="margin-top:1rem">
|
||||
<h3>Move to another storage</h3>
|
||||
<p class="form-hint">You can move this app’s data to another connected storage. The app stops briefly during the move, and the data is deleted from the old place only after the check and a successful restart.</p>
|
||||
<div style="display:flex;gap:.5rem;align-items:center;flex-wrap:wrap;margin-top:.5rem">
|
||||
<select id="app-migrate-target" class="btn btn-sm btn-outline">
|
||||
<option value="">Choose a target storage…</option>
|
||||
<option value="/mnt/felhom-drives/scratch_hdd">SCRATCH HDD (/mnt/felhom-drives/scratch_hdd)</option>
|
||||
</select>
|
||||
<button class="btn btn-sm btn-outline" onclick="appMigrate(this,'glance','Glance')">Move</button>
|
||||
</div>
|
||||
<div id="app-migrate-progress" style="display:none;margin-top:.75rem;padding:.75rem;border:1px solid var(--blue);border-radius:6px;background:rgba(0,136,204,0.06)"></div>
|
||||
</div>
|
||||
<script>
|
||||
function appMigFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; }
|
||||
function appMigRender(job){
|
||||
var names={stop:'Stopping',copy:'Copying data',verify:'Checking',flip:'Reinstalling',redeploy:'Reinstalling',cleanup:'Deleting old data'};
|
||||
var s=names[job.phase]||job.phase;
|
||||
if(job.phase==='copy'&&job.bytes_total>0){ s+=' ('+Math.floor(100*job.bytes_done/job.bytes_total)+'% — '+appMigFmtGB(job.bytes_done)+'/'+appMigFmtGB(job.bytes_total)+')'; }
|
||||
return s+'…';
|
||||
}
|
||||
function appMigWatch(){
|
||||
var panel=document.getElementById('app-migrate-progress');
|
||||
if(panel) panel.style.display='block';
|
||||
function tick(){
|
||||
fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){
|
||||
var job=d.data&&d.data.job;
|
||||
if(!job){ if(panel) panel.textContent='No move in progress.'; return; }
|
||||
if(panel) panel.innerHTML=appMigRender(job);
|
||||
if(job.phase==='done'){ if(panel) panel.innerHTML+='<br><strong>Done</strong>'; setTimeout(function(){location.reload();},1500); return; }
|
||||
if(job.phase==='aborted'){ if(panel) panel.innerHTML+='<br><strong style="color:var(--danger,#c0392b)">Stopped: '+(job.error||'')+'</strong><br>The old data is untouched.'; return; }
|
||||
setTimeout(tick,1500);
|
||||
}).catch(function(){ setTimeout(tick,2000); });
|
||||
}
|
||||
tick();
|
||||
}
|
||||
function appMigrate(btn,app,label){
|
||||
var sel=document.getElementById('app-migrate-target');
|
||||
var target=sel?sel.value:'';
|
||||
if(!target){ showAlert('Choose a target storage.'); return; }
|
||||
felhomConfirm(btn,'Move the '+label+' data to: '+target+'? The app stops briefly. The old data is deleted only after a successful move.',function(){
|
||||
fetch('/api/storage/migrate-app',{method:'POST',headers:Object.assign({'Content-Type':'application/json'},csrfHeaders()),body:JSON.stringify({app:app,target:target})})
|
||||
.then(function(r){return r.json();}).then(function(d){ if(d.ok){ appMigWatch(); } else { showAlert('Error: '+(d.error||'unknown')); } })
|
||||
.catch(function(e){ showAlert('Error: '+e); });
|
||||
});
|
||||
}
|
||||
(function(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ if(d.data&&d.data.job){ appMigWatch(); } }).catch(function(){}); })();
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<div class="app-info-grid">
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>What is it for?</h3>
|
||||
<ul class="app-info-list">
|
||||
<li>RSS news, YouTube, Reddit and more on a single page</li><li>Weather and system widgets</li><li>Keep your bookmarks and favourite sites in order</li><li>Very fast, and it asks almost nothing of the machine</li><li>A layout you set yourself, in a YAML file</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>First steps</h3>
|
||||
<ol class="app-info-list">
|
||||
<li>Open dashboard.DOMAIN in your browser</li><li>Edit the glance.yml configuration file in the volume</li><li>Add your RSS feeds and widgets</li><li>Arrange the layout the way you want it</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>Documentation</h3>
|
||||
<p><a href="https://github.com/glanceapp/glance/blob/main/docs/configuration.md" target="_blank" class="app-info-link">Official documentation ↗</a></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
(function(){
|
||||
var burger=document.querySelector('.nav-burger');
|
||||
var sidebar=document.getElementById('sidebar');
|
||||
var backdrop=document.querySelector('.nav-backdrop');
|
||||
if(!burger||!sidebar||!backdrop)return;
|
||||
function setOpen(open){
|
||||
sidebar.classList.toggle('is-open',open);
|
||||
document.body.classList.toggle('nav-open',open);
|
||||
backdrop.hidden=!open;
|
||||
burger.setAttribute('aria-expanded',open?'true':'false');
|
||||
}
|
||||
burger.addEventListener('click',function(){setOpen(!sidebar.classList.contains('is-open'));});
|
||||
backdrop.addEventListener('click',function(){setOpen(false);});
|
||||
document.addEventListener('keydown',function(e){
|
||||
if(e.key==='Escape'&&sidebar.classList.contains('is-open'))setOpen(false);
|
||||
});
|
||||
})();
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.closest('a, button, .btn, input, select, textarea, .app-row-actions, .stack-detail-actions')) return;
|
||||
var card = e.target.closest('[data-href]');
|
||||
if (card) window.location.href = card.dataset.href;
|
||||
});
|
||||
async function checkBeforeDeploy(e, name) {
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name);
|
||||
var data = await resp.json();
|
||||
if (data.ok && data.data && data.data.deployed) {
|
||||
e.preventDefault();
|
||||
showAlert('This app is already installed.');
|
||||
window.location.reload();
|
||||
return false;
|
||||
}
|
||||
} catch(err) {}
|
||||
return true;
|
||||
}
|
||||
async function syncTemplates() {
|
||||
const btn = document.getElementById('sync-btn');
|
||||
const toast = document.getElementById('sync-toast');
|
||||
if (!btn) return;
|
||||
const origText = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '↻ Refreshing...';
|
||||
btn.classList.add('loading');
|
||||
try {
|
||||
const resp = await fetch('/api/sync', {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders())
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (toast) {
|
||||
toast.textContent = data.ok ? (data.message || 'Templates refreshed') : ('Error: ' + (data.error || 'Unknown error'));
|
||||
toast.className = 'sync-toast ' + (data.ok ? 'sync-toast-ok' : 'sync-toast-err');
|
||||
toast.style.display = 'block';
|
||||
setTimeout(function() { toast.style.display = 'none'; }, 5000);
|
||||
}
|
||||
if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) {
|
||||
setTimeout(function() { window.location.reload(); }, 1500);
|
||||
}
|
||||
} catch (err) {
|
||||
if (toast) {
|
||||
toast.textContent = 'Network error: ' + err.message;
|
||||
toast.className = 'sync-toast sync-toast-err';
|
||||
toast.style.display = 'block';
|
||||
setTimeout(function() { toast.style.display = 'none'; }, 5000);
|
||||
}
|
||||
}
|
||||
btn.innerHTML = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
}
|
||||
async function stackAction(event, name, action) {
|
||||
const btn = event.currentTarget;
|
||||
const origText = btn.textContent;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'In progress...';
|
||||
btn.classList.add('loading');
|
||||
try {
|
||||
const resp = await fetch('/api/stacks/' + name + '/' + action, {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders())
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (!data.ok) {
|
||||
showAlert('Error: ' + (data.error || 'Unknown error'));
|
||||
btn.textContent = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (action === 'update') {
|
||||
followUpdate(name, btn);
|
||||
return;
|
||||
}
|
||||
window.location.reload();
|
||||
} catch (err) {
|
||||
showAlert('Network error: ' + err.message);
|
||||
btn.textContent = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
}
|
||||
}
|
||||
function followUpdate(name, btn) {
|
||||
var started = Date.now();
|
||||
var timer = setInterval(async function() {
|
||||
if (Date.now() - started > 30 * 60 * 1000) { clearInterval(timer); window.location.reload(); return; }
|
||||
try {
|
||||
var r = await fetch('/api/stacks/' + name);
|
||||
var d = await r.json();
|
||||
if (!d.ok || !d.data) return;
|
||||
if (d.data.update_phase_label) btn.textContent = d.data.update_phase_label;
|
||||
if (!d.data.updating) { clearInterval(timer); window.location.reload(); }
|
||||
} catch (e) {}
|
||||
}, 3000);
|
||||
}
|
||||
async function deleteOrphanStack(name) {
|
||||
var modal = document.createElement('div');
|
||||
modal.className = 'modal-overlay';
|
||||
modal.id = 'delete-modal';
|
||||
modal.innerHTML = '<div class="modal-card"><h3>Loading...</h3></div>';
|
||||
modal.addEventListener('click', function(e) { if (e.target === modal) closeDeleteModal(); });
|
||||
document.body.appendChild(modal);
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name + '/hdd-data');
|
||||
var data = await resp.json();
|
||||
var hddInfo = '';
|
||||
var checkboxHTML = '';
|
||||
if (data.ok && data.data && data.data.has_hdd_data) {
|
||||
hddInfo = '<div class="modal-hdd-info"><strong>Your data on the hard drive:</strong>';
|
||||
data.data.hdd_paths.forEach(function(p) {
|
||||
hddInfo += '<div class="modal-hdd-path">' + p.path + ' (' + (p.exists ? p.size_human : 'does not exist') + ')</div>';
|
||||
});
|
||||
hddInfo += '</div>';
|
||||
checkboxHTML = '<label class="modal-checkbox"><input type="checkbox" id="delete-hdd-check"> Delete your data from the hard drive</label>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Delete app: ' + name + '</h3>' +
|
||||
'<p style="color:var(--text-2);font-size:.9rem;margin-bottom:.75rem">This removes the containers, the volumes and the configuration files.</p>' +
|
||||
'<div class="alert alert-warning" style="margin-bottom:.75rem">You cannot undo this!</div>' +
|
||||
hddInfo + checkboxHTML +
|
||||
'<div class="modal-actions">' +
|
||||
'<button class="btn btn-outline" onclick="closeDeleteModal()">Cancel</button>' +
|
||||
'<button class="btn btn-danger" id="confirm-delete-btn" onclick="confirmDelete(\'' + name + '\')">Delete</button>' +
|
||||
'</div>';
|
||||
} catch (err) {
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Error</h3><p style="color:var(--text-2)">Could not load the details: ' + err.message + '</p>' +
|
||||
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeDeleteModal()">Close</button></div>';
|
||||
}
|
||||
}
|
||||
function closeDeleteModal() {
|
||||
var modal = document.getElementById('delete-modal');
|
||||
if (modal) modal.remove();
|
||||
}
|
||||
async function confirmDelete(name) {
|
||||
var btn = document.getElementById('confirm-delete-btn');
|
||||
var checkbox = document.getElementById('delete-hdd-check');
|
||||
var removeHDD = checkbox ? checkbox.checked : false;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Deleting...';
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name, {
|
||||
method: 'DELETE',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({remove_hdd_data: removeHDD})
|
||||
});
|
||||
var data = await resp.json();
|
||||
if (data.ok) {
|
||||
var modal = document.getElementById('delete-modal');
|
||||
var removedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) {
|
||||
removedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Deleted data: ' + data.data.hdd_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
var preservedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) {
|
||||
preservedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Kept data: ' + data.data.hdd_paths_preserved.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.hdd_note) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">' + data.data.hdd_note + '</p>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Deleted!</h3>' +
|
||||
'<p style="color:var(--text-2)">The app (' + name + ') was deleted.</p>' +
|
||||
removedInfo + preservedInfo +
|
||||
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Close</button></div>';
|
||||
} else {
|
||||
showAlert('Error: ' + (data.error || 'Unknown error'));
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Delete';
|
||||
}
|
||||
} catch (err) {
|
||||
showAlert('Network error: ' + err.message);
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Delete';
|
||||
}
|
||||
}
|
||||
async function removeStack(name) {
|
||||
var modal = document.createElement('div');
|
||||
modal.className = 'modal-overlay';
|
||||
modal.id = 'remove-modal';
|
||||
modal.innerHTML = '<div class="modal-card"><h3>Loading...</h3></div>';
|
||||
modal.addEventListener('click', function(e) { if (e.target === modal) closeRemoveModal(); });
|
||||
document.body.appendChild(modal);
|
||||
try {
|
||||
var [hddResp, backupResp] = await Promise.all([
|
||||
fetch('/api/stacks/' + name + '/hdd-data').then(function(r) { return r.json(); }),
|
||||
fetch('/api/stacks/' + name + '/backup-data').then(function(r) { return r.json(); })
|
||||
]);
|
||||
var sections = '';
|
||||
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Always deleted:</strong>' +
|
||||
'<ul style="margin:.25rem 0;padding-left:1.2rem;color:var(--text-2);font-size:.85rem">' +
|
||||
'<li>Docker volumes (database, app configuration)</li>' +
|
||||
'<li>Install configuration (app.yaml)</li>' +
|
||||
'<li>Secondary backup schedule</li>' +
|
||||
'</ul></div>';
|
||||
|
||||
var hddCheckbox = '';
|
||||
if (hddResp.ok && hddResp.data && hddResp.data.has_hdd_data) {
|
||||
var hddPaths = '';
|
||||
hddResp.data.hdd_paths.forEach(function(p) {
|
||||
hddPaths += '<div class="modal-hdd-path">' + p.path + ' (' + (p.exists ? p.size_human : 'does not exist') + ')</div>';
|
||||
});
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Your data on the hard drive:</strong>' + hddPaths +
|
||||
'<label class="modal-checkbox"><input type="checkbox" id="remove-hdd-check"> Delete your data</label>' +
|
||||
'<div class="alert alert-info" style="margin-top:.5rem;font-size:.8rem" id="remove-hdd-keep-warning">' +
|
||||
'If you reinstall the app, you need to import the data again, because the database is deleted. Data you keep is NOT backed up automatically any more.' +
|
||||
'</div></div>';
|
||||
hddCheckbox = '\x3Cscript>document.getElementById("remove-hdd-check").addEventListener("change",function(){document.getElementById("remove-hdd-keep-warning").style.display=this.checked?"none":"";});<\/script>';
|
||||
}
|
||||
|
||||
var backupCheckbox = '';
|
||||
if (backupResp.ok && backupResp.data && backupResp.data.has_backups) {
|
||||
var bkPaths = '';
|
||||
backupResp.data.backup_paths.forEach(function(p) {
|
||||
if (p.exists) bkPaths += '<div class="modal-hdd-path">' + p.path + ' (' + p.size_human + ')</div>';
|
||||
});
|
||||
if (bkPaths) {
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Backup data:</strong>' + bkPaths +
|
||||
'<label class="modal-checkbox"><input type="checkbox" id="remove-backup-check"> Delete backup data</label>' +
|
||||
'<div class="alert alert-info" style="margin-top:.5rem;font-size:.8rem">' +
|
||||
'The nightly restic snapshots cannot be deleted one by one — they expire automatically under the retention rules.' +
|
||||
'</div></div>';
|
||||
}
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Remove app: ' + name + '</h3>' +
|
||||
'<p style="color:var(--text-2);font-size:.9rem;margin-bottom:.75rem">The app goes back to "Not installed". The template stays, and you can install it again.</p>' +
|
||||
'<div class="alert alert-warning" style="margin-bottom:.75rem">You cannot undo this!</div>' +
|
||||
sections +
|
||||
'<div class="modal-actions">' +
|
||||
'<button class="btn btn-outline" onclick="closeRemoveModal()">Cancel</button>' +
|
||||
'<button class="btn btn-danger" id="confirm-remove-btn" onclick="confirmRemoveStack(\'' + name + '\')">Remove</button>' +
|
||||
'</div>' + hddCheckbox;
|
||||
} catch (err) {
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Error</h3><p style="color:var(--text-2)">Could not load the details: ' + err.message + '</p>' +
|
||||
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeRemoveModal()">Close</button></div>';
|
||||
}
|
||||
}
|
||||
function closeRemoveModal() {
|
||||
var modal = document.getElementById('remove-modal');
|
||||
if (modal) modal.remove();
|
||||
}
|
||||
async function confirmRemoveStack(name) {
|
||||
var btn = document.getElementById('confirm-remove-btn');
|
||||
var hddCheck = document.getElementById('remove-hdd-check');
|
||||
var backupCheck = document.getElementById('remove-backup-check');
|
||||
var removeHDD = hddCheck ? hddCheck.checked : false;
|
||||
var removeBackups = backupCheck ? backupCheck.checked : false;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Removing...';
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name + '/remove', {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({remove_hdd_data: removeHDD, remove_backups: removeBackups})
|
||||
});
|
||||
var data = await resp.json();
|
||||
if (data.ok) {
|
||||
var modal = document.getElementById('remove-modal');
|
||||
var removedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) {
|
||||
removedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Deleted data: ' + data.data.hdd_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.backup_paths_removed && data.data.backup_paths_removed.length > 0) {
|
||||
removedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Deleted backups: ' + data.data.backup_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
var preservedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) {
|
||||
preservedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Kept data: ' + data.data.hdd_paths_preserved.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.hdd_note) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">' + data.data.hdd_note + '</p>';
|
||||
}
|
||||
if (data.data && data.data.backup_paths_refused && data.data.backup_paths_refused.length > 0) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Backups not deleted: ' + data.data.backup_paths_refused.join('; ') + '</p>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Removed!</h3>' +
|
||||
'<p style="color:var(--text-2)">The app (' + name + ') was removed. You can install it again with the Install button.</p>' +
|
||||
removedInfo + preservedInfo +
|
||||
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Close</button></div>';
|
||||
} else {
|
||||
showAlert('Error: ' + (data.error || 'Unknown error'));
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Remove';
|
||||
}
|
||||
} catch (err) {
|
||||
showAlert('Network error: ' + err.message);
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Remove';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
+664
@@ -0,0 +1,664 @@
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="hu" class="no-js">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Glance — Felhom.eu</title>
|
||||
|
||||
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg?v=0.261.0">
|
||||
<link rel="stylesheet" href="/static/style.css?v=0.261.0">
|
||||
<meta name="csrf-token" content="fab572e2f6d427f4d84e1174a6a9ad4d6c7730fc751a7ab6c826734b03ecf8c2">
|
||||
<script>
|
||||
|
||||
|
||||
document.documentElement.className=document.documentElement.className.replace('no-js','js');
|
||||
function csrfHeaders(){var el=document.querySelector('meta[name="csrf-token"]');return el?{'X-CSRF-Token':el.content}:{};}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function felhomConfirm(el,question,onYes){
|
||||
if(!el||el.dataset.fcOpen)return;
|
||||
el.dataset.fcOpen='1';
|
||||
var wrap=document.createElement('span');wrap.className='inline-confirm';
|
||||
var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question;
|
||||
var yes=document.createElement('button');yes.type='button';yes.className='btn btn-xs btn-danger';yes.textContent='Igen';
|
||||
var no=document.createElement('button');no.type='button';no.className='btn btn-xs btn-outline';no.textContent='Mégse';
|
||||
wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no);
|
||||
el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling);
|
||||
function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;}
|
||||
no.addEventListener('click',close);
|
||||
yes.addEventListener('click',function(){close();onYes();});
|
||||
}
|
||||
document.addEventListener('click',function(e){
|
||||
var btn=e.target.closest?e.target.closest('[data-confirm]'):null;
|
||||
if(!btn)return;
|
||||
e.preventDefault();
|
||||
felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){
|
||||
var form=btn.closest('form');
|
||||
if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
document.addEventListener('click',function(e){
|
||||
var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null;
|
||||
if(!btn)return;
|
||||
var group=btn.closest('.nav-group');
|
||||
if(!group)return;
|
||||
var willOpen=!group.classList.contains('is-open');
|
||||
document.querySelectorAll('.nav-group.is-open').forEach(function(g){
|
||||
g.classList.remove('is-open');
|
||||
var t=g.querySelector('.nav-group-toggle');
|
||||
if(t)t.setAttribute('aria-expanded','false');
|
||||
});
|
||||
if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');}
|
||||
});
|
||||
function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML='<h3>Üzenet</h3><pre style="white-space:pre-wrap;word-break:break-word;background:var(--bg-2);padding:.75rem;border-radius:.375rem;font-size:.85rem;max-height:60vh;overflow-y:auto;user-select:text;cursor:text">'+msg.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')+'</pre><div class="modal-actions"><button class="btn btn-primary" onclick="document.getElementById(\'alert-modal\').remove()">OK</button></div>';o.appendChild(c);document.body.appendChild(o);}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" style="display:none" aria-hidden="true">
|
||||
<symbol id="i-hard-drive" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 16h.01" /> <path d="M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" /> <path d="M21.946 12.013H2.054" /> <path d="M6 16h.01" /></symbol>
|
||||
<symbol id="i-cpu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20v2" /> <path d="M12 2v2" /> <path d="M17 20v2" /> <path d="M17 2v2" /> <path d="M2 12h2" /> <path d="M2 17h2" /> <path d="M2 7h2" /> <path d="M20 12h2" /> <path d="M20 17h2" /> <path d="M20 7h2" /> <path d="M7 20v2" /> <path d="M7 2v2" /> <rect x="4" y="4" width="16" height="16" rx="2" /> <rect x="8" y="8" width="8" height="8" rx="1" /></symbol>
|
||||
<symbol id="i-memory-stick" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 12v-2" /> <path d="M12 18v-2" /> <path d="M16 12v-2" /> <path d="M16 18v-2" /> <path d="M2 11h1.5" /> <path d="M20 18v-2" /> <path d="M20.5 11H22" /> <path d="M4 18v-2" /> <path d="M8 12v-2" /> <path d="M8 18v-2" /> <rect x="2" y="6" width="20" height="10" rx="2" /></symbol>
|
||||
<symbol id="i-thermometer" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 4v10.54a4 4 0 1 1-4 0V4a2 2 0 0 1 4 0Z" /></symbol>
|
||||
<symbol id="i-triangle-alert" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" /> <path d="M12 9v4" /> <path d="M12 17h.01" /></symbol>
|
||||
<symbol id="i-lock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="11" x="3" y="11" rx="2" ry="2" /> <path d="M7 11V7a5 5 0 0 1 10 0v4" /></symbol>
|
||||
<symbol id="i-cloud" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z" /></symbol>
|
||||
<symbol id="i-square" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" /></symbol>
|
||||
<symbol id="i-rotate-cw" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" /> <path d="M21 3v5h-5" /></symbol>
|
||||
<symbol id="i-file-text" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /> <path d="M14 2v5a1 1 0 0 0 1 1h5" /> <path d="M10 9H8" /> <path d="M16 13H8" /> <path d="M16 17H8" /></symbol>
|
||||
<symbol id="i-external-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6" /> <path d="M10 14 21 3" /> <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></symbol>
|
||||
<symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /></symbol>
|
||||
<symbol id="i-server" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="8" x="2" y="2" rx="2" ry="2" /> <rect width="20" height="8" x="2" y="14" rx="2" ry="2" /> <line x1="6" x2="6.01" y1="6" y2="6" /> <line x1="6" x2="6.01" y1="18" y2="18" /></symbol>
|
||||
<symbol id="i-share" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3" /> <circle cx="6" cy="12" r="3" /> <circle cx="18" cy="19" r="3" /> <line x1="8.59" x2="15.42" y1="13.51" y2="17.49" /> <line x1="15.41" x2="8.59" y1="6.51" y2="10.49" /></symbol>
|
||||
<symbol id="i-layout-grid" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="7" x="3" y="3" rx="1" /> <rect width="7" height="7" x="14" y="3" rx="1" /> <rect width="7" height="7" x="14" y="14" rx="1" /> <rect width="7" height="7" x="3" y="14" rx="1" /></symbol>
|
||||
<symbol id="i-rocket" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91 0z" /> <path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z" /> <path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0" /> <path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5" /></symbol>
|
||||
<symbol id="i-settings" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /> <circle cx="12" cy="12" r="3" /></symbol>
|
||||
<symbol id="i-bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.268 21a2 2 0 0 0 3.464 0" /> <path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" /></symbol>
|
||||
<symbol id="i-x" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18" /> <path d="m6 6 12 12" /></symbol>
|
||||
<symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5" /></symbol>
|
||||
<symbol id="i-pencil" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" /> <path d="m15 5 4 4" /></symbol>
|
||||
<symbol id="i-info" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /> <path d="M12 16v-4" /> <path d="M12 8h.01" /></symbol>
|
||||
<symbol id="i-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 15V3" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /> <path d="m7 10 5 5 5-5" /></symbol>
|
||||
<symbol id="i-upload" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v12" /> <path d="m17 8-5-5-5 5" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /></symbol>
|
||||
<symbol id="i-trash-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 11v6" /> <path d="M14 11v6" /> <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /> <path d="M3 6h18" /> <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></symbol>
|
||||
<symbol id="i-wrench" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z" /></symbol>
|
||||
<symbol id="i-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" /> <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" /></symbol>
|
||||
<symbol id="i-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34" /> <circle cx="11" cy="11" r="8" /></symbol>
|
||||
<symbol id="i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14" /> <path d="M12 5v14" /></symbol>
|
||||
<symbol id="i-chevron-down" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6" /></symbol>
|
||||
<symbol id="i-play" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z" /></symbol>
|
||||
<symbol id="i-pause" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="14" y="3" width="5" height="18" rx="1" /> <rect x="5" y="3" width="5" height="18" rx="1" /></symbol>
|
||||
<symbol id="i-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h16" /> <path d="M4 6h16" /> <path d="M4 18h16" /></symbol>
|
||||
</svg>
|
||||
<header class="mobile-topbar">
|
||||
<a href="/" class="mobile-topbar-logo"><img src="/static/felhom-logo.svg?v=0.261.0" alt="Felhom.eu"></a>
|
||||
<button type="button" class="nav-burger" aria-expanded="false" aria-controls="sidebar" aria-label="Menü">
|
||||
<svg class="ico"><use href="#i-menu"/></svg>
|
||||
</button>
|
||||
</header>
|
||||
<div class="nav-backdrop" hidden></div>
|
||||
<nav class="sidebar" id="sidebar">
|
||||
<div class="sidebar-header">
|
||||
<img src="/static/felhom-logo.svg?v=0.261.0" alt="Felhom.eu" class="sidebar-logo">
|
||||
</div>
|
||||
<ul class="nav-links">
|
||||
<li><a href="/launcher" class=""><svg class="ico"><use href="#i-rocket"/></svg>Indítópult</a></li>
|
||||
<li><a href="/dashboard" class=""><svg class="ico"><use href="#i-layout-grid"/></svg>Vezérlőpult</a></li>
|
||||
<li><a href="/stacks" class="active"><svg class="ico"><use href="#i-cloud"/></svg>Alkalmazások</a></li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-storage"><svg class="ico"><use href="#i-hard-drive"/></svg>Tárhely<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-storage" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/storage" class="">Meghajtók</a></li>
|
||||
<li><a href="/storage/network" class="">Hálózati tárhely</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-backups"><svg class="ico"><use href="#i-shield"/></svg>Biztonsági mentés<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-backups" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/backups" class="">Áttekintés</a></li>
|
||||
<li><a href="/backups/remote" class="">Távoli mentés</a></li>
|
||||
<li><a href="/backups/apps" class="">Alkalmazások</a></li>
|
||||
<li><a href="/backups/restore" class="">Visszaállítás</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-group">
|
||||
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-sharing"><svg class="ico"><use href="#i-share"/></svg>Megosztás<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
||||
<ul id="nav-group-sharing" class="nav-links nav-links-sub nav-links-nested">
|
||||
<li><a href="/sharing" class="">Hálózati megosztás</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="/monitoring" class=""><svg class="ico"><use href="#i-cpu"/></svg>Rendszermonitor</a></li>
|
||||
<li><a href="/debug" class=""><svg class="ico"><use href="#i-wrench"/></svg>Debug</a></li>
|
||||
</ul>
|
||||
<div class="sidebar-bottom">
|
||||
<div class="nav-group-label">Beállítások</div>
|
||||
<ul class="nav-links nav-links-sub">
|
||||
<li><a href="/settings" class=""><svg class="ico"><use href="#i-settings"/></svg>Rendszer</a></li>
|
||||
<li><a href="/settings/notifications" class=""><svg class="ico"><use href="#i-bell"/></svg>Értesítések</a></li>
|
||||
<li><a href="/settings/security" class=""><svg class="ico"><use href="#i-lock"/></svg>Biztonság és hozzáférés</a></li>
|
||||
</ul>
|
||||
<div class="sidebar-footer">
|
||||
<span class="version">0.261.0</span><details class="lang-globe">
|
||||
<summary class="lang-globe-btn" aria-label="Nyelv" title="Nyelv"><svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10" /><path d="M2 12h20" /><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" /></svg></summary>
|
||||
<ul class="lang-globe-menu">
|
||||
<li><form method="POST" action="/settings/language"><input type="hidden" name="_csrf" value="fab572e2f6d427f4d84e1174a6a9ad4d6c7730fc751a7ab6c826734b03ecf8c2"><input type="hidden" name="back" value="/apps/glance"><button type="submit" name="lang" value="hu" lang="hu" class="lang-globe-item current" aria-current="true">Magyar</button></form></li>
|
||||
<li><form method="POST" action="/settings/language"><input type="hidden" name="_csrf" value="fab572e2f6d427f4d84e1174a6a9ad4d6c7730fc751a7ab6c826734b03ecf8c2"><input type="hidden" name="back" value="/apps/glance"><button type="submit" name="lang" value="en" lang="en" class="lang-globe-item">English</button></form></li>
|
||||
</ul>
|
||||
</details>
|
||||
<a href="/logout" class="logout-link">Kijelentkezés ↗</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<main class="content">
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="alerts-container">
|
||||
|
||||
|
||||
<div class="alert-banner alert-banner-warning">
|
||||
<span class="alert-icon"><svg class="ico"><use href="#i-triangle-alert"/></svg></span>
|
||||
<span class="alert-message">Telepített alkalmazás nem fut: Glance (stopped)</span>
|
||||
<a href="/monitoring" class="alert-link">Rendszermonitor →</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="alert-banner alert-banner-warning">
|
||||
<span class="alert-icon"><svg class="ico"><use href="#i-triangle-alert"/></svg></span>
|
||||
<span class="alert-message">Hub kapcsolat kikapcsolva — a központi monitoring nem aktív</span>
|
||||
<a href="/monitoring" class="alert-link">Rendszermonitor →</a>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="page-header">
|
||||
<div style="display:flex;align-items:center;gap:1rem">
|
||||
<a href="/stacks" class="btn btn-sm btn-outline">← Alkalmazások</a>
|
||||
<h2>Glance</h2>
|
||||
</div>
|
||||
<div style="display:flex;align-items:center;gap:.5rem">
|
||||
|
||||
<span class="stack-state-badge state-neutral">Leállítva</span>
|
||||
|
||||
|
||||
<span class="tag tag-warn" title="Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.">Frissítés elérhető — ma</span>
|
||||
<a href="https://dashboard.enkisfelhom.hu" target="_blank" class="btn btn-sm btn-outline">Megnyitás ↗</a>
|
||||
<a href="/stacks/glance/logs" class="btn btn-sm btn-outline">Napló</a>
|
||||
|
||||
<a href="/stacks/glance/export" class="btn btn-sm btn-outline">Exportálás</a>
|
||||
<a href="/stacks/glance/deploy" class="btn btn-sm btn-outline">Beállítások</a>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="alert alert-error" style="margin-top:1rem" data-held="true">A(z) glance frissítése 2026-09-21 21:53-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:47 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza. <a href="/backups/apps" class="btn btn-sm btn-outline">Mentések</a></div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-hero">
|
||||
<img class="app-info-logo" src="/static/assets/glance-logo.svg"
|
||||
alt="Glance" data-fallback="/static/app-placeholder.svg"
|
||||
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/glance-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
||||
<div class="app-info-hero-text">
|
||||
|
||||
<p class="app-info-tagline">Minimalista dashboard - RSS, időjárás, könyvjelzők egy oldalon</p>
|
||||
|
||||
<div class="stack-meta-badges">
|
||||
<span class="meta-badge">~20M RAM</span>
|
||||
<span class="meta-badge">dashboard</span>
|
||||
|
||||
<span class="meta-badge meta-badge-ok">Pi kompatibilis</span>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="app-screenshots" id="screenshots">
|
||||
<img src="/static/assets/glance-screenshot-1.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
<img src="/static/assets/glance-screenshot-2.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
<img src="/static/assets/glance-screenshot-3.webp" alt="" class="app-screenshot"
|
||||
onerror="this.style.display='none'">
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card" style="margin-top:1rem">
|
||||
<h3>Áthelyezés másik tárhelyre</h3>
|
||||
<p class="form-hint">Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át. Az alkalmazás az áthelyezés alatt rövid időre leáll, az adatok pedig csak az ellenőrzés és a sikeres újraindítás után törlődnek a régi helyről.</p>
|
||||
<div style="display:flex;gap:.5rem;align-items:center;flex-wrap:wrap;margin-top:.5rem">
|
||||
<select id="app-migrate-target" class="btn btn-sm btn-outline">
|
||||
<option value="">Válassz céltárhelyet…</option>
|
||||
<option value="/mnt/felhom-drives/scratch_hdd">SCRATCH HDD (/mnt/felhom-drives/scratch_hdd)</option>
|
||||
</select>
|
||||
<button class="btn btn-sm btn-outline" onclick="appMigrate(this,'glance','Glance')">Áthelyezés</button>
|
||||
</div>
|
||||
<div id="app-migrate-progress" style="display:none;margin-top:.75rem;padding:.75rem;border:1px solid var(--blue);border-radius:6px;background:rgba(0,136,204,0.06)"></div>
|
||||
</div>
|
||||
<script>
|
||||
function appMigFmtGB(b){ return (Number(b||0)/1e9).toFixed(1)+' GB'; }
|
||||
function appMigRender(job){
|
||||
var names={stop:'Leállítás',copy:'Adatok másolása',verify:'Ellenőrzés',flip:'Újratelepítés',redeploy:'Újratelepítés',cleanup:'Régi adatok törlése'};
|
||||
var s=names[job.phase]||job.phase;
|
||||
if(job.phase==='copy'&&job.bytes_total>0){ s+=' ('+Math.floor(100*job.bytes_done/job.bytes_total)+'% — '+appMigFmtGB(job.bytes_done)+'/'+appMigFmtGB(job.bytes_total)+')'; }
|
||||
return s+'…';
|
||||
}
|
||||
function appMigWatch(){
|
||||
var panel=document.getElementById('app-migrate-progress');
|
||||
if(panel) panel.style.display='block';
|
||||
function tick(){
|
||||
fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){
|
||||
var job=d.data&&d.data.job;
|
||||
if(!job){ if(panel) panel.textContent='Nincs folyamatban áthelyezés.'; return; }
|
||||
if(panel) panel.innerHTML=appMigRender(job);
|
||||
if(job.phase==='done'){ if(panel) panel.innerHTML+='<br><strong>Kész</strong>'; setTimeout(function(){location.reload();},1500); return; }
|
||||
if(job.phase==='aborted'){ if(panel) panel.innerHTML+='<br><strong style="color:var(--danger,#c0392b)">Megszakadt: '+(job.error||'')+'</strong><br>A régi adatok érintetlenek.'; return; }
|
||||
setTimeout(tick,1500);
|
||||
}).catch(function(){ setTimeout(tick,2000); });
|
||||
}
|
||||
tick();
|
||||
}
|
||||
function appMigrate(btn,app,label){
|
||||
var sel=document.getElementById('app-migrate-target');
|
||||
var target=sel?sel.value:'';
|
||||
if(!target){ showAlert('Válassz céltárhelyet.'); return; }
|
||||
felhomConfirm(btn,'Áthelyezed a(z) '+label+' adatait ide: '+target+'? Az alkalmazás rövid időre leáll. A régi adatok csak sikeres áthelyezés után törlődnek.',function(){
|
||||
fetch('/api/storage/migrate-app',{method:'POST',headers:Object.assign({'Content-Type':'application/json'},csrfHeaders()),body:JSON.stringify({app:app,target:target})})
|
||||
.then(function(r){return r.json();}).then(function(d){ if(d.ok){ appMigWatch(); } else { showAlert('Hiba: '+(d.error||'ismeretlen')); } })
|
||||
.catch(function(e){ showAlert('Hiba: '+e); });
|
||||
});
|
||||
}
|
||||
(function(){ fetch('/api/storage/migrate/status').then(function(r){return r.json();}).then(function(d){ if(d.data&&d.data.job){ appMigWatch(); } }).catch(function(){}); })();
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<div class="app-info-grid">
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>Mire használható?</h3>
|
||||
<ul class="app-info-list">
|
||||
<li>RSS hírek, YouTube, Reddit és egyéb tartalmak egy oldalon</li><li>Időjárás és rendszer monitorozás widgetekkel</li><li>Könyvjelzők és kedvenc oldalak rendszerezése</li><li>Ultragyors, minimális erőforrás igény</li><li>Személyre szabható elrendezés YAML konfigurációval</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>Első lépések</h3>
|
||||
<ol class="app-info-list">
|
||||
<li>Nyisd meg a dashboard.DOMAIN címet a böngészőben</li><li>Szerkeszd a glance.yml konfigurációs fájlt a kötetben</li><li>Add hozzá az RSS feedeket, widgeteket</li><li>Személyre szabd az elrendezést</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="app-info-card">
|
||||
<h3>Dokumentáció</h3>
|
||||
<p><a href="https://github.com/glanceapp/glance/blob/main/docs/configuration.md" target="_blank" class="app-info-link">Hivatalos dokumentáció ↗</a></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</main>
|
||||
<script>
|
||||
|
||||
|
||||
|
||||
(function(){
|
||||
var burger=document.querySelector('.nav-burger');
|
||||
var sidebar=document.getElementById('sidebar');
|
||||
var backdrop=document.querySelector('.nav-backdrop');
|
||||
if(!burger||!sidebar||!backdrop)return;
|
||||
function setOpen(open){
|
||||
sidebar.classList.toggle('is-open',open);
|
||||
document.body.classList.toggle('nav-open',open);
|
||||
backdrop.hidden=!open;
|
||||
burger.setAttribute('aria-expanded',open?'true':'false');
|
||||
}
|
||||
burger.addEventListener('click',function(){setOpen(!sidebar.classList.contains('is-open'));});
|
||||
backdrop.addEventListener('click',function(){setOpen(false);});
|
||||
document.addEventListener('keydown',function(e){
|
||||
if(e.key==='Escape'&&sidebar.classList.contains('is-open'))setOpen(false);
|
||||
});
|
||||
})();
|
||||
document.addEventListener('click', function(e) {
|
||||
if (e.target.closest('a, button, .btn, input, select, textarea, .app-row-actions, .stack-detail-actions')) return;
|
||||
var card = e.target.closest('[data-href]');
|
||||
if (card) window.location.href = card.dataset.href;
|
||||
});
|
||||
async function checkBeforeDeploy(e, name) {
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name);
|
||||
var data = await resp.json();
|
||||
if (data.ok && data.data && data.data.deployed) {
|
||||
e.preventDefault();
|
||||
showAlert('Ez az alkalmazás már telepítve van.');
|
||||
window.location.reload();
|
||||
return false;
|
||||
}
|
||||
} catch(err) {}
|
||||
return true;
|
||||
}
|
||||
async function syncTemplates() {
|
||||
const btn = document.getElementById('sync-btn');
|
||||
const toast = document.getElementById('sync-toast');
|
||||
if (!btn) return;
|
||||
const origText = btn.innerHTML;
|
||||
btn.disabled = true;
|
||||
btn.innerHTML = '↻ Frissítés...';
|
||||
btn.classList.add('loading');
|
||||
try {
|
||||
const resp = await fetch('/api/sync', {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders())
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (toast) {
|
||||
toast.textContent = data.ok ? (data.message || 'Sablonok frissítve') : ('Hiba: ' + (data.error || 'Ismeretlen hiba'));
|
||||
toast.className = 'sync-toast ' + (data.ok ? 'sync-toast-ok' : 'sync-toast-err');
|
||||
toast.style.display = 'block';
|
||||
setTimeout(function() { toast.style.display = 'none'; }, 5000);
|
||||
}
|
||||
if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) {
|
||||
setTimeout(function() { window.location.reload(); }, 1500);
|
||||
}
|
||||
} catch (err) {
|
||||
if (toast) {
|
||||
toast.textContent = 'Hálózati hiba: ' + err.message;
|
||||
toast.className = 'sync-toast sync-toast-err';
|
||||
toast.style.display = 'block';
|
||||
setTimeout(function() { toast.style.display = 'none'; }, 5000);
|
||||
}
|
||||
}
|
||||
btn.innerHTML = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
}
|
||||
async function stackAction(event, name, action) {
|
||||
const btn = event.currentTarget;
|
||||
const origText = btn.textContent;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Folyamatban...';
|
||||
btn.classList.add('loading');
|
||||
try {
|
||||
const resp = await fetch('/api/stacks/' + name + '/' + action, {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders())
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (!data.ok) {
|
||||
showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba'));
|
||||
btn.textContent = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (action === 'update') {
|
||||
followUpdate(name, btn);
|
||||
return;
|
||||
}
|
||||
window.location.reload();
|
||||
} catch (err) {
|
||||
showAlert('Hálózati hiba: ' + err.message);
|
||||
btn.textContent = origText;
|
||||
btn.disabled = false;
|
||||
btn.classList.remove('loading');
|
||||
}
|
||||
}
|
||||
function followUpdate(name, btn) {
|
||||
var started = Date.now();
|
||||
var timer = setInterval(async function() {
|
||||
if (Date.now() - started > 30 * 60 * 1000) { clearInterval(timer); window.location.reload(); return; }
|
||||
try {
|
||||
var r = await fetch('/api/stacks/' + name);
|
||||
var d = await r.json();
|
||||
if (!d.ok || !d.data) return;
|
||||
if (d.data.update_phase_label) btn.textContent = d.data.update_phase_label;
|
||||
if (!d.data.updating) { clearInterval(timer); window.location.reload(); }
|
||||
} catch (e) {}
|
||||
}, 3000);
|
||||
}
|
||||
async function deleteOrphanStack(name) {
|
||||
var modal = document.createElement('div');
|
||||
modal.className = 'modal-overlay';
|
||||
modal.id = 'delete-modal';
|
||||
modal.innerHTML = '<div class="modal-card"><h3>Betöltés...</h3></div>';
|
||||
modal.addEventListener('click', function(e) { if (e.target === modal) closeDeleteModal(); });
|
||||
document.body.appendChild(modal);
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name + '/hdd-data');
|
||||
var data = await resp.json();
|
||||
var hddInfo = '';
|
||||
var checkboxHTML = '';
|
||||
if (data.ok && data.data && data.data.has_hdd_data) {
|
||||
hddInfo = '<div class="modal-hdd-info"><strong>Felhasználói adatok a merevlemezen:</strong>';
|
||||
data.data.hdd_paths.forEach(function(p) {
|
||||
hddInfo += '<div class="modal-hdd-path">' + p.path + ' (' + (p.exists ? p.size_human : 'nem létezik') + ')</div>';
|
||||
});
|
||||
hddInfo += '</div>';
|
||||
checkboxHTML = '<label class="modal-checkbox"><input type="checkbox" id="delete-hdd-check"> Felhasználói adatok törlése a merevlemezről</label>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Alkalmazás törlése: ' + name + '</h3>' +
|
||||
'<p style="color:var(--text-2);font-size:.9rem;margin-bottom:.75rem">Ez a művelet eltávolítja a konténereket, a köteteket és a konfigurációs fájlokat.</p>' +
|
||||
'<div class="alert alert-warning" style="margin-bottom:.75rem">Ez a művelet nem visszavonható!</div>' +
|
||||
hddInfo + checkboxHTML +
|
||||
'<div class="modal-actions">' +
|
||||
'<button class="btn btn-outline" onclick="closeDeleteModal()">Mégsem</button>' +
|
||||
'<button class="btn btn-danger" id="confirm-delete-btn" onclick="confirmDelete(\'' + name + '\')">Törlés</button>' +
|
||||
'</div>';
|
||||
} catch (err) {
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Hiba</h3><p style="color:var(--text-2)">Nem sikerült lekérni az adatokat: ' + err.message + '</p>' +
|
||||
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeDeleteModal()">Bezárás</button></div>';
|
||||
}
|
||||
}
|
||||
function closeDeleteModal() {
|
||||
var modal = document.getElementById('delete-modal');
|
||||
if (modal) modal.remove();
|
||||
}
|
||||
async function confirmDelete(name) {
|
||||
var btn = document.getElementById('confirm-delete-btn');
|
||||
var checkbox = document.getElementById('delete-hdd-check');
|
||||
var removeHDD = checkbox ? checkbox.checked : false;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Törlés folyamatban...';
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name, {
|
||||
method: 'DELETE',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({remove_hdd_data: removeHDD})
|
||||
});
|
||||
var data = await resp.json();
|
||||
if (data.ok) {
|
||||
var modal = document.getElementById('delete-modal');
|
||||
var removedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) {
|
||||
removedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Törölt adatok: ' + data.data.hdd_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
var preservedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) {
|
||||
preservedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Megőrzött adatok: ' + data.data.hdd_paths_preserved.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.hdd_note) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">' + data.data.hdd_note + '</p>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Sikeresen törölve!</h3>' +
|
||||
'<p style="color:var(--text-2)">Az alkalmazás (' + name + ') törölve lett.</p>' +
|
||||
removedInfo + preservedInfo +
|
||||
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Bezárás</button></div>';
|
||||
} else {
|
||||
showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba'));
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Törlés';
|
||||
}
|
||||
} catch (err) {
|
||||
showAlert('Hálózati hiba: ' + err.message);
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Törlés';
|
||||
}
|
||||
}
|
||||
async function removeStack(name) {
|
||||
var modal = document.createElement('div');
|
||||
modal.className = 'modal-overlay';
|
||||
modal.id = 'remove-modal';
|
||||
modal.innerHTML = '<div class="modal-card"><h3>Betöltés...</h3></div>';
|
||||
modal.addEventListener('click', function(e) { if (e.target === modal) closeRemoveModal(); });
|
||||
document.body.appendChild(modal);
|
||||
try {
|
||||
var [hddResp, backupResp] = await Promise.all([
|
||||
fetch('/api/stacks/' + name + '/hdd-data').then(function(r) { return r.json(); }),
|
||||
fetch('/api/stacks/' + name + '/backup-data').then(function(r) { return r.json(); })
|
||||
]);
|
||||
var sections = '';
|
||||
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Mindig törlődik:</strong>' +
|
||||
'<ul style="margin:.25rem 0;padding-left:1.2rem;color:var(--text-2);font-size:.85rem">' +
|
||||
'<li>Docker kötetek (adatbázis, alkalmazás konfiguráció)</li>' +
|
||||
'<li>Telepítési konfiguráció (app.yaml)</li>' +
|
||||
'<li>Másodlagos mentés ütemezése</li>' +
|
||||
'</ul></div>';
|
||||
|
||||
var hddCheckbox = '';
|
||||
if (hddResp.ok && hddResp.data && hddResp.data.has_hdd_data) {
|
||||
var hddPaths = '';
|
||||
hddResp.data.hdd_paths.forEach(function(p) {
|
||||
hddPaths += '<div class="modal-hdd-path">' + p.path + ' (' + (p.exists ? p.size_human : 'nem létezik') + ')</div>';
|
||||
});
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Felhasználói adatok a merevlemezen:</strong>' + hddPaths +
|
||||
'<label class="modal-checkbox"><input type="checkbox" id="remove-hdd-check"> Felhasználói adatok törlése</label>' +
|
||||
'<div class="alert alert-info" style="margin-top:.5rem;font-size:.8rem" id="remove-hdd-keep-warning">' +
|
||||
'Ha újratelepíti az alkalmazást, az adatokat újra importálnia kell, mivel az adatbázis törlődik. A megtartott adatok a továbbiakban NEM lesznek automatikusan mentve.' +
|
||||
'</div></div>';
|
||||
hddCheckbox = '\x3Cscript>document.getElementById("remove-hdd-check").addEventListener("change",function(){document.getElementById("remove-hdd-keep-warning").style.display=this.checked?"none":"";});<\/script>';
|
||||
}
|
||||
|
||||
var backupCheckbox = '';
|
||||
if (backupResp.ok && backupResp.data && backupResp.data.has_backups) {
|
||||
var bkPaths = '';
|
||||
backupResp.data.backup_paths.forEach(function(p) {
|
||||
if (p.exists) bkPaths += '<div class="modal-hdd-path">' + p.path + ' (' + p.size_human + ')</div>';
|
||||
});
|
||||
if (bkPaths) {
|
||||
sections += '<div class="modal-section">' +
|
||||
'<strong>Mentési adatok:</strong>' + bkPaths +
|
||||
'<label class="modal-checkbox"><input type="checkbox" id="remove-backup-check"> Mentési adatok törlése</label>' +
|
||||
'<div class="alert alert-info" style="margin-top:.5rem;font-size:.8rem">' +
|
||||
'Az éjszakai restic pillanatképek nem törölhetők egyenként — a megőrzési szabályok szerint automatikusan elavulnak.' +
|
||||
'</div></div>';
|
||||
}
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Alkalmazás eltávolítása: ' + name + '</h3>' +
|
||||
'<p style="color:var(--text-2);font-size:.9rem;margin-bottom:.75rem">Az alkalmazás visszaáll "Nincs telepítve" állapotba. A sablon megmarad, újratelepíthető.</p>' +
|
||||
'<div class="alert alert-warning" style="margin-bottom:.75rem">Ez a művelet nem visszavonható!</div>' +
|
||||
sections +
|
||||
'<div class="modal-actions">' +
|
||||
'<button class="btn btn-outline" onclick="closeRemoveModal()">Mégsem</button>' +
|
||||
'<button class="btn btn-danger" id="confirm-remove-btn" onclick="confirmRemoveStack(\'' + name + '\')">Eltávolítás</button>' +
|
||||
'</div>' + hddCheckbox;
|
||||
} catch (err) {
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Hiba</h3><p style="color:var(--text-2)">Nem sikerült lekérni az adatokat: ' + err.message + '</p>' +
|
||||
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeRemoveModal()">Bezárás</button></div>';
|
||||
}
|
||||
}
|
||||
function closeRemoveModal() {
|
||||
var modal = document.getElementById('remove-modal');
|
||||
if (modal) modal.remove();
|
||||
}
|
||||
async function confirmRemoveStack(name) {
|
||||
var btn = document.getElementById('confirm-remove-btn');
|
||||
var hddCheck = document.getElementById('remove-hdd-check');
|
||||
var backupCheck = document.getElementById('remove-backup-check');
|
||||
var removeHDD = hddCheck ? hddCheck.checked : false;
|
||||
var removeBackups = backupCheck ? backupCheck.checked : false;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Eltávolítás folyamatban...';
|
||||
try {
|
||||
var resp = await fetch('/api/stacks/' + name + '/remove', {
|
||||
method: 'POST',
|
||||
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
||||
body: JSON.stringify({remove_hdd_data: removeHDD, remove_backups: removeBackups})
|
||||
});
|
||||
var data = await resp.json();
|
||||
if (data.ok) {
|
||||
var modal = document.getElementById('remove-modal');
|
||||
var removedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) {
|
||||
removedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Törölt adatok: ' + data.data.hdd_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.backup_paths_removed && data.data.backup_paths_removed.length > 0) {
|
||||
removedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Törölt mentések: ' + data.data.backup_paths_removed.join(', ') + '</p>';
|
||||
}
|
||||
var preservedInfo = '';
|
||||
if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) {
|
||||
preservedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Megőrzött adatok: ' + data.data.hdd_paths_preserved.join(', ') + '</p>';
|
||||
}
|
||||
if (data.data && data.data.hdd_note) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">' + data.data.hdd_note + '</p>';
|
||||
}
|
||||
if (data.data && data.data.backup_paths_refused && data.data.backup_paths_refused.length > 0) {
|
||||
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Nem törölt mentések: ' + data.data.backup_paths_refused.join('; ') + '</p>';
|
||||
}
|
||||
modal.querySelector('.modal-card').innerHTML =
|
||||
'<h3>Sikeresen eltávolítva!</h3>' +
|
||||
'<p style="color:var(--text-2)">Az alkalmazás (' + name + ') eltávolítva. Újratelepíthető a Telepítés gombbal.</p>' +
|
||||
removedInfo + preservedInfo +
|
||||
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Bezárás</button></div>';
|
||||
} else {
|
||||
showAlert('Hiba: ' + (data.error || 'Ismeretlen hiba'));
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Eltávolítás';
|
||||
}
|
||||
} catch (err) {
|
||||
showAlert('Hálózati hiba: ' + err.message);
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Eltávolítás';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
21:45:02 ==== B1: the UNATTENDED HOLD
|
||||
21:45:02 [0] deployed apps not level with the catalog BEFORE the leg: ['adventurelog', 'docmost', 'glance', 'tandoor', 'uptime-kuma', 'wishlist']
|
||||
21:45:02 [0] removing adventurelog so the caller has only one app to react to (installed={'adventurelog': 'ghcr.io/seanmorley15/adventurelog-backend:v0.12.1', 'adventurelog-frontend': 'ghcr.io/seanmorley15/adventurelog-frontend:v0.12.1', 'adventurelog-postgres': 'postgis/postgis:16-3.5-alpine'} catalog={'adventurelog': 'ghcr.io/seanmorley15/adventurelog-backend:v0.13.0', 'adventurelog-frontend': 'ghcr.io/seanmorley15/adventurelog-frontend:v0.13.0', 'adventurelog-postgres': 'postgis/postgis:16-3.5-alpine'})
|
||||
21:45:06 [X] stop -> 200 {'ok': True, 'message': 'Stack adventurelog stop completed'}
|
||||
21:45:11 [X] remove (with drive data) -> 200 {'ok': True, 'data': {'removed': 'adventurelog', 'volumes_removed': [], 'hdd_paths_removed': [], 'hdd_paths_preserved': [], 'hdd_note': 'Az alkalmazás nem tárol
|
||||
21:45:18 [X] after remove: deployed=False leftovers='/opt/docker/stacks/adventurelog'
|
||||
21:45:18 [0] removing docmost so the caller has only one app to react to (installed={'docmost': 'docmost/docmost:0.96.0', 'docmost-postgres': 'postgres:16-alpine', 'docmost-redis': 'redis:7-alpine'} catalog={'docmost': 'docmost/docmost:0.96.0', 'docmost-postgres': 'postgres:17-alpine', 'docmost-redis': 'redis:7-alpine'})
|
||||
21:45:20 [X] stop -> 200 {'ok': True, 'message': 'Stack docmost stop completed'}
|
||||
21:45:25 [X] remove (with drive data) -> 200 {'ok': True, 'data': {'removed': 'docmost', 'volumes_removed': [], 'hdd_paths_removed': [], 'hdd_paths_preserved': [], 'hdd_note': 'Az alkalmazás nem tárolt saj
|
||||
21:45:33 [X] after remove: deployed=False leftovers='/opt/docker/stacks/docmost'
|
||||
21:45:33 [0] removing tandoor so the caller has only one app to react to (installed={'tandoor': 'ghcr.io/tandoorrecipes/recipes:2.6.13', 'tandoor-postgres': 'postgres:16-alpine'} catalog={'tandoor': 'ghcr.io/tandoorrecipes/recipes:2.6.15', 'tandoor-postgres': 'postgres:16-alpine'})
|
||||
21:45:36 [X] stop -> 200 {'ok': True, 'message': 'Stack tandoor stop completed'}
|
||||
21:45:41 [X] remove (with drive data) -> 200 {'ok': True, 'data': {'removed': 'tandoor', 'volumes_removed': [], 'hdd_paths_removed': [], 'hdd_paths_preserved': [], 'hdd_note': 'Az alkalmazás nem tárolt saj
|
||||
21:45:48 [X] after remove: deployed=False leftovers='/opt/docker/stacks/tandoor'
|
||||
21:45:48 [0] removing uptime-kuma so the caller has only one app to react to (installed={'uptime-kuma': 'louislam/uptime-kuma:2.5.1'} catalog={'uptime-kuma': 'louislam/uptime-kuma:2.5.5'})
|
||||
21:45:53 [X] stop -> 200 {'ok': True, 'message': 'Stack uptime-kuma stop completed'}
|
||||
21:45:58 [X] remove (with drive data) -> 200 {'ok': True, 'data': {'removed': 'uptime-kuma', 'volumes_removed': ['uptime-kuma_uptime_kuma_data'], 'hdd_paths_removed': [], 'hdd_paths_preserved': [], 'hdd_no
|
||||
21:46:05 [X] after remove: deployed=False leftovers='/opt/docker/stacks/uptime-kuma'
|
||||
21:46:05 [0] removing wishlist so the caller has only one app to react to (installed={'wishlist': 'ghcr.io/cmintey/wishlist:v0.67.0'} catalog={'wishlist': 'ghcr.io/cmintey/wishlist:v0.66.0'})
|
||||
21:46:15 [X] stop -> 200 {'ok': True, 'message': 'Stack wishlist stop completed'}
|
||||
21:46:21 [X] remove (with drive data) -> 200 {'ok': True, 'data': {'removed': 'wishlist', 'volumes_removed': ['wishlist_wishlist_data', 'wishlist_wishlist_uploads'], 'hdd_paths_removed': [], 'hdd_paths_pre
|
||||
21:46:28 [X] after remove: deployed=False leftovers='/opt/docker/stacks/wishlist'
|
||||
21:46:28 [0] swept before the leg: ['adventurelog', 'docmost', 'tandoor', 'uptime-kuma', 'wishlist']
|
||||
21:46:28 [1] removing the existing glance so it is redeployed from the drill image store
|
||||
21:46:29 [X] stop -> 200 {'ok': True, 'message': 'Stack glance stop completed'}
|
||||
21:46:34 [X] remove (with drive data) -> 200 {'ok': True, 'data': {'removed': 'glance', 'volumes_removed': ['glance_glance_config'], 'hdd_paths_removed': [], 'hdd_paths_preserved': [], 'hdd_note': 'Az alka
|
||||
21:46:41 [X] after remove: deployed=False leftovers='/opt/docker/stacks/glance'
|
||||
21:46:42 [5] drill commit b906b38169d8: glance glanceapp/glance:v0.8.5 -> localhost:5000/drill/glance:1.0.0 (push rc=0)
|
||||
21:46:47 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
|
||||
21:46:57 [1] deployed, controller state=running, pinned={'glance': 'localhost:5000/drill/glance:1.0.0'}
|
||||
21:46:59 [1] live compose now: image: localhost:5000/drill/glance:1.0.0
|
||||
21:47:03 [4] „Mentés most" -> 200 {'ok': True, 'message': 'Mentés elindítva'}
|
||||
21:47:44 [4] backup idle; last=None
|
||||
21:47:44 [5] drill commit c644164ee439: glance localhost:5000/drill/glance:1.0.0 -> localhost:5000/drill/glance:1.0.1 (push rc=0)
|
||||
21:47:49 [3] badge HU: [{'title': 'Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.', 'text': 'Frissítés elérhető — ma'}]
|
||||
21:47:49 [3] badge EN: [{'title': 'A newer version of this app is available. Select the Update button to start it.', 'text': 'Update available — today'}]
|
||||
21:47:49 [4] running unattended-caller.py, 3 passes, 90 s apart — nobody presses anything
|
||||
21:56:03 [4] caller exited rc=0 after 494.3s
|
||||
21:56:03 2026-09-21T19:47:49Z glance BEHIND and within a major — pressing Update
|
||||
21:56:03 2026-09-21T19:47:49Z glance: phase=safety-dump updating=True
|
||||
21:56:03 2026-09-21T19:47:51Z glance: phase=verifying updating=True
|
||||
21:56:03 2026-09-21T19:53:02Z glance: phase=failed updating=False
|
||||
21:56:03 2026-09-21T19:53:02Z glance ENDED held after 312.9s
|
||||
21:56:03 2026-09-21T19:56:03Z === summary outcomes={'glance': ('held', 312.9)} never_again=['glance']
|
||||
21:56:05 [5] state=stopped phase=failed error='A(z) glance frissítése 2026-09-21 21:53-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:47 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.' hold='A(z) glance frissítése 2026-09-21 21:53-kor nem sikerült, és az alkalmazás nem indult el az új verzióval. Az alkalmazás biztonsági okból leállítva marad, hogy az adatai ne sérüljenek. Visszaállítható a Mentések oldalon ebből a biztonsági mentésből: saját meghajtó, 2026-09-21 21:47 — ez a másolat a beállításokat, az adatbázist és az adatköteteket tartalmazza.'
|
||||
21:56:05 [5] pinned={'glance': 'localhost:5000/drill/glance:1.0.1'}
|
||||
21:56:05 [5] installed={'glance': 'localhost:5000/drill/glance:1.0.0'}
|
||||
21:56:05 [5] inspect=[]
|
||||
@@ -0,0 +1,12 @@
|
||||
21:56:06 ==== B2: the new tag cannot be pulled
|
||||
21:56:07 [5] drill commit 23cb04a29d63: bentopdf ghcr.io/alam00000/bentopdf:v2.8.6 -> localhost:5000/drill/pdf:1.0.0 (push rc=0)
|
||||
21:56:11 [1] deploy -> 202 {'ok': True, 'message': 'Telepítés elindítva – az állapot a kártyán követhető'}
|
||||
21:56:26 [1] deployed, controller state=running, pinned={'bentopdf': 'ghcr.io/alam00000/bentopdf:v2.8.6'}
|
||||
21:56:26 [4] „Mentés most" -> 200 {'ok': True, 'message': 'Mentés elindítva'}
|
||||
21:57:06 [4] backup idle; last=None
|
||||
21:57:10 [5] drill commit 90ff4da2ba20: bentopdf localhost:5000/drill/pdf:1.0.0 -> localhost:5000/drill/pdf:1.0.1 (push rc=0)
|
||||
21:57:14 badge HU [{'title': 'Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.', 'text': 'Frissítés elérhető — ma'}]
|
||||
21:57:14 [6] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követhető'}
|
||||
21:57:14 + 0.0s phase=safety-dump label=Adatbázis pillanatkép… err=None hold=None
|
||||
21:57:15 + 1.0s phase=failed label=A frissítés nem sikerült err=Az új verzió letöltése nem sikerült, ezért a frissítés elmaradt. Az alkalmazás a korábbi verzióval fut tovább. hold=None
|
||||
21:57:20 second press (to read the refusal on the wire): http=202 {"ok": true, "data": {"accepted": true, "completed": false}, "message": "Frissítés elindult – az állapot a kártyán követhető"}
|
||||
@@ -0,0 +1,383 @@
|
||||
{
|
||||
"leg": "B2",
|
||||
"app": "bentopdf",
|
||||
"edge": "localhost:5000/drill/pdf:1.0.0 -> localhost:5000/drill/pdf:1.0.1 (tag absent from the store)",
|
||||
"badges": {
|
||||
"hu": [
|
||||
{
|
||||
"title": "Újabb változat érhető el ehhez az alkalmazáshoz. A frissítés indításához nyomd meg a Frissítés gombot.",
|
||||
"text": "Frissítés elérhető — ma"
|
||||
}
|
||||
],
|
||||
"en": [
|
||||
{
|
||||
"title": "A newer version of this app is available. Select the Update button to start it.",
|
||||
"text": "Update available — today"
|
||||
}
|
||||
]
|
||||
},
|
||||
"phases": {
|
||||
"accepted": true,
|
||||
"http": "202",
|
||||
"phases": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"phase": "safety-dump",
|
||||
"label": "Adatbázis pillanatkép…",
|
||||
"updating": true,
|
||||
"error": null,
|
||||
"hold": null
|
||||
},
|
||||
{
|
||||
"t": 1.0,
|
||||
"phase": "failed",
|
||||
"label": "A frissítés nem sikerült",
|
||||
"updating": false,
|
||||
"error": "Az új verzió letöltése nem sikerült, ezért a frissítés elmaradt. Az alkalmazás a korábbi verzióval fut tovább.",
|
||||
"hold": null
|
||||
}
|
||||
],
|
||||
"duration_s": 3.1,
|
||||
"final_phase": "failed",
|
||||
"update_error": "Az új verzió letöltése nem sikerült, ezért a frissítés elmaradt. Az alkalmazás a korábbi verzióval fut tovább.",
|
||||
"hold_reason": null,
|
||||
"state": "running"
|
||||
},
|
||||
"before": {
|
||||
"label": "before",
|
||||
"at": "2026-09-21T21:57:06",
|
||||
"state": "running",
|
||||
"updating": false,
|
||||
"update_phase": null,
|
||||
"update_error": null,
|
||||
"hold_reason": null,
|
||||
"observables": {
|
||||
"pinned_images": {
|
||||
"bentopdf": "ghcr.io/alam00000/bentopdf:v2.8.6"
|
||||
},
|
||||
"installed_images": {
|
||||
"bentopdf": "ghcr.io/alam00000/bentopdf:v2.8.6"
|
||||
},
|
||||
"catalog_images": null,
|
||||
"live_compose_image_lines": [
|
||||
"image: ghcr.io/alam00000/bentopdf:v2.8.6"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"bentopdf ghcr.io/alam00000/bentopdf:v2.8.6 running=true restarts=0"
|
||||
]
|
||||
}
|
||||
},
|
||||
"after": {
|
||||
"label": "after",
|
||||
"at": "2026-09-21T21:57:17",
|
||||
"state": "running",
|
||||
"updating": false,
|
||||
"update_phase": "failed",
|
||||
"update_error": "Az új verzió letöltése nem sikerült, ezért a frissítés elmaradt. Az alkalmazás a korábbi verzióval fut tovább.",
|
||||
"hold_reason": null,
|
||||
"observables": {
|
||||
"pinned_images": {
|
||||
"bentopdf": "ghcr.io/alam00000/bentopdf:v2.8.6"
|
||||
},
|
||||
"installed_images": {
|
||||
"bentopdf": "ghcr.io/alam00000/bentopdf:v2.8.6"
|
||||
},
|
||||
"catalog_images": {
|
||||
"bentopdf": "localhost:5000/drill/pdf:1.0.1"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: ghcr.io/alam00000/bentopdf:v2.8.6"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"bentopdf ghcr.io/alam00000/bentopdf:v2.8.6 running=true restarts=0"
|
||||
]
|
||||
}
|
||||
},
|
||||
"old_version_still_serving": true,
|
||||
"time_to_steady_s": 3.4,
|
||||
"sentences": {
|
||||
"hu": [
|
||||
"BentoPDF — Felhom.eu Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Telepített alkalmazás nem fut: Glance (stopped) Rendszermonitor → Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások BentoPDF Fut Frissítés elérhető — ma Megnyitás ↗ Napló Exportálás Beállítások Adatbázis pillanatkép… Adatvédelmi fókuszú PDF eszköztár - a fájlok soha nem hagyják el a szervered ~100M RAM tools Pi kompatibilis Áthelyezés másik tárhelyre Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át.",
|
||||
"PDF összefűzés, szétválasztás és forgatás PDF konvertálás képekké és képekből Jelszóvédelem és titkosítás Vízjel hozzáadása dokumentumokhoz Minden feldolgozás a szerveren - a fájlok nem kerülnek külső szolgáltatóhoz Első lépések Nyisd meg a pdf.DOMAIN címet a böngészőben Válaszd ki a kívánt műveletet Töltsd fel a PDF fájlt és kattints a feldolgozásra Dokumentáció Hivatalos dokumentáció ↗"
|
||||
],
|
||||
"en": [
|
||||
"BentoPDF — Felhom.eu Launcher Dashboard Apps Storage Drives Network storage Backup Overview Remote backup Apps Restore Sharing Network sharing System monitor Debug Settings System Notifications Security and access 0.261.0 Magyar English Sign out ↗ An installed app is not running: Glance (stopped) System monitor → The hub connection is off — central monitoring is not running System monitor → ← Apps BentoPDF Running Update available — today Open ↗ Log Export Settings Adatbázis pillanatkép… A privacy-first PDF toolbox - your files never leave your own server ~100M RAM tools Runs on Pi Move to another storage You can move this app’s data to another connected storage.",
|
||||
"Merge, split and rotate PDFs Convert PDFs to images and images to PDFs Password protection and encryption Add a watermark to a document Everything happens on your own server - no file goes to an outside service First steps Open pdf.DOMAIN in your browser Choose what you want to do Upload the PDF and start it Documentation Official documentation ↗"
|
||||
]
|
||||
},
|
||||
"second_press": {
|
||||
"http": "202",
|
||||
"body": {
|
||||
"ok": true,
|
||||
"data": {
|
||||
"accepted": true,
|
||||
"completed": false
|
||||
},
|
||||
"message": "Frissítés elindult – az állapot a kártyán követhető"
|
||||
}
|
||||
},
|
||||
"events": {
|
||||
"path": "/api/debug/dump",
|
||||
"body": {
|
||||
"alerts": [
|
||||
{
|
||||
"ID": "deadapp-4a2adff2",
|
||||
"Level": "warning",
|
||||
"Message": "Telepített alkalmazás nem fut: Glance (stopped)",
|
||||
"MessageKey": "alert.deadapp.single_state",
|
||||
"MessageArgs": [
|
||||
"Glance",
|
||||
"stopped"
|
||||
],
|
||||
"Link": "/monitoring",
|
||||
"LinkText": "Rendszermonitor",
|
||||
"LinkTextKey": "alert.link.monitoring",
|
||||
"PageOnly": null,
|
||||
"Inline": false
|
||||
},
|
||||
{
|
||||
"ID": "hub-disabled",
|
||||
"Level": "warning",
|
||||
"Message": "Hub kapcsolat kikapcsolva — a központi monitoring nem aktív",
|
||||
"MessageKey": "alert.hub.disabled",
|
||||
"MessageArgs": null,
|
||||
"Link": "/monitoring",
|
||||
"LinkText": "Rendszermonitor",
|
||||
"LinkTextKey": "alert.link.monitoring",
|
||||
"PageOnly": null,
|
||||
"Inline": false
|
||||
}
|
||||
],
|
||||
"backup": {
|
||||
"enabled": true,
|
||||
"last_db_dump": {
|
||||
"success": true,
|
||||
"time": "2026-09-21T19:57:02.581012164Z"
|
||||
},
|
||||
"running": false
|
||||
},
|
||||
"controller": {
|
||||
"config_hash": "ce32a35aaefc739e2a23db3df1c962a77bc672aa723119939a61af9a771a5a0e",
|
||||
"logging_level": "debug",
|
||||
"pid": 1,
|
||||
"uptime_seconds": 6457,
|
||||
"version": "0.261.0"
|
||||
},
|
||||
"health": {
|
||||
"issues": null,
|
||||
"status": "ok",
|
||||
"warnings": null
|
||||
},
|
||||
"hub": {
|
||||
"enabled": false,
|
||||
"url": ""
|
||||
},
|
||||
"network": {
|
||||
"default_route": "error: docker exec ip -4 route show default: exit status 1",
|
||||
"dns_servers": "error: docker exec cat /etc/resolv.conf: exit status 1",
|
||||
"interfaces": "error: docker exec ip -o link show: exit status 1",
|
||||
"lan_address": ""
|
||||
},
|
||||
"notifications": {
|
||||
"cooldown_hours": 6,
|
||||
"email": "",
|
||||
"enabled_events": [
|
||||
"backup_failed",
|
||||
"db_dump_failed",
|
||||
"disk_warning",
|
||||
"disk_critical",
|
||||
"storage_disconnected",
|
||||
"node_down",
|
||||
"health_critical",
|
||||
"expected_backup_missed",
|
||||
"expected_dbdump_missed",
|
||||
"offbox_enlarge_blocked"
|
||||
]
|
||||
},
|
||||
"scheduler": [
|
||||
{
|
||||
"interval": "10s",
|
||||
"last_run": "2026-09-21T19:57:13.329915946Z",
|
||||
"name": "status-refresh",
|
||||
"running": false,
|
||||
"type": "every"
|
||||
},
|
||||
{
|
||||
"interval": "2m0s",
|
||||
"last_run": "2026-09-21T19:55:43.382300008Z",
|
||||
"name": "stack-scan",
|
||||
"running": false,
|
||||
"type": "every"
|
||||
},
|
||||
{
|
||||
"interval": "10s",
|
||||
"last_run": "2026-09-21T19:57:13.336537811Z",
|
||||
"name": "health-probes",
|
||||
"running": false,
|
||||
"type": "every"
|
||||
},
|
||||
{
|
||||
"interval": "5m0s",
|
||||
"last_run": "2026-09-21T19:54:43.454536004Z",
|
||||
"name": "system-health",
|
||||
"running": false,
|
||||
"type": "every"
|
||||
},
|
||||
{
|
||||
"interval": "30s",
|
||||
"last_run": "2026-09-21T19:57:13.359505204Z",
|
||||
"name": "deadapp-check",
|
||||
"running": false,
|
||||
"type": "every"
|
||||
},
|
||||
{
|
||||
"interval": "30s",
|
||||
"last_run": "2026-09-21T19:57:13.323014613Z",
|
||||
"name": "ring-spill",
|
||||
"running": false,
|
||||
"type": "every"
|
||||
},
|
||||
{
|
||||
"name": "db-dump",
|
||||
"running": false,
|
||||
"schedule": "02:30",
|
||||
"type": "daily"
|
||||
},
|
||||
{
|
||||
"interval": "5m0s",
|
||||
"last_run": "2026-09-21T19:54:43.304884532Z",
|
||||
"name": "offsite-credential-retry",
|
||||
"running": false,
|
||||
"type": "every"
|
||||
},
|
||||
{
|
||||
"interval": "5m0s",
|
||||
"last_run": "2026-09-21T19:54:43.427874885Z",
|
||||
"name": "backup-cache",
|
||||
"running": false,
|
||||
"type": "every"
|
||||
},
|
||||
{
|
||||
"name": "tier2-backup",
|
||||
"running": false,
|
||||
"schedule": "03:30",
|
||||
"type": "daily"
|
||||
},
|
||||
{
|
||||
"name": "offbox-backup",
|
||||
"running": false,
|
||||
"schedule": "04:15",
|
||||
"type": "daily"
|
||||
},
|
||||
{
|
||||
"name": "offsite-abandon-sweep",
|
||||
"running": false,
|
||||
"schedule": "05:10",
|
||||
"type": "daily"
|
||||
},
|
||||
{
|
||||
"name": "offsite-integrity",
|
||||
"running": false,
|
||||
"schedule": "06:00",
|
||||
"type": "daily"
|
||||
},
|
||||
{
|
||||
"name": "offsite-proof",
|
||||
"running": false,
|
||||
"schedule": "05:30",
|
||||
"type": "daily"
|
||||
},
|
||||
{
|
||||
"name": "metrics-prune",
|
||||
"running": false,
|
||||
"schedule": "04:00",
|
||||
"type": "daily"
|
||||
},
|
||||
{
|
||||
"name": "fill-watch",
|
||||
"running": false,
|
||||
"schedule": "03:30",
|
||||
"type": "daily"
|
||||
}
|
||||
],
|
||||
"self_update": {
|
||||
"enabled": false
|
||||
},
|
||||
"stacks": {
|
||||
"deployed": 7,
|
||||
"list": [
|
||||
{
|
||||
"containers": [
|
||||
"bentopdf"
|
||||
],
|
||||
"display_name": "BentoPDF",
|
||||
"name": "bentopdf",
|
||||
"state": "running"
|
||||
},
|
||||
{
|
||||
"containers": [
|
||||
"gitea"
|
||||
],
|
||||
"display_name": "Gitea",
|
||||
"name": "gitea",
|
||||
"state": "running"
|
||||
},
|
||||
{
|
||||
"containers": [],
|
||||
"display_name": "Glance",
|
||||
"name": "glance",
|
||||
"state": "stopped"
|
||||
},
|
||||
{
|
||||
"containers": [
|
||||
"nextcloud",
|
||||
"nextcloud-db",
|
||||
"nextcloud-redis"
|
||||
],
|
||||
"display_name": "Nextcloud",
|
||||
"name": "nextcloud",
|
||||
"state": "running"
|
||||
},
|
||||
{
|
||||
"containers": [
|
||||
"opengist"
|
||||
],
|
||||
"display_name": "OpenGist",
|
||||
"name": "opengist",
|
||||
"state": "running"
|
||||
},
|
||||
{
|
||||
"containers": [
|
||||
"vaultwarden"
|
||||
],
|
||||
"display_name": "Vaultwarden",
|
||||
"name": "vaultwarden",
|
||||
"state": "running"
|
||||
},
|
||||
{
|
||||
"containers": [
|
||||
"zipline",
|
||||
"zipline-postgres"
|
||||
],
|
||||
"display_name": "Zipline",
|
||||
"name": "zipline",
|
||||
"state": "unhealthy"
|
||||
}
|
||||
],
|
||||
"running": 9,
|
||||
"stopped": 0
|
||||
},
|
||||
"storage": [
|
||||
{
|
||||
"decommissioned": false,
|
||||
"disconnected": false,
|
||||
"label": "SCRATCH HDD",
|
||||
"path": "/mnt/felhom-drives/scratch_hdd",
|
||||
"total_gb": 937.8239631652832,
|
||||
"used_gb": 48.98271942138672,
|
||||
"used_percent": 5.223018534956538
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
21:57:20 ==== B3: Update pressed while a backup is running
|
||||
21:57:21 „Mentés most" -> 200 {'ok': True, 'message': 'Mentés elindítva'}
|
||||
21:57:22 press 0: http=409 reason='busy' :: A frissítés most nem indítható: mentés/visszaállítás folyamatban. Próbáld újra, ha befejeződött.
|
||||
21:57:25 press 1: http=409 reason='busy' :: A frissítés most nem indítható: mentés/visszaállítás folyamatban. Próbáld újra, ha befejeződött.
|
||||
21:57:28 press 2: http=409 reason='busy' :: A frissítés most nem indítható: mentés/visszaállítás folyamatban. Próbáld újra, ha befejeződött.
|
||||
21:57:31 press 3: http=409 reason='busy' :: A frissítés most nem indítható: mentés/visszaállítás folyamatban. Próbáld újra, ha befejeződött.
|
||||
21:57:34 press 4: http=409 reason='busy' :: A frissítés most nem indítható: mentés/visszaállítás folyamatban. Próbáld újra, ha befejeződött.
|
||||
21:57:37 press 5: http=409 reason='busy' :: A frissítés most nem indítható: mentés/visszaállítás folyamatban. Próbáld újra, ha befejeződött.
|
||||
21:58:00 after the backup finished: http=202 reason=None ::
|
||||
21:58:04 the update that got through ended: {'final_phase': 'failed', 'update_error': 'Az új verzió letöltése nem sikerült, ezért a frissítés elmaradt. Az alkalmazás a korábbi verzióval fut tovább.', 'hold_reason': None, 'seconds': 2.0, 'observables': {'pinned_images': {'bentopdf': 'ghcr.io/alam00000/bentopdf:v2.8.6'}, 'installed_images': {'bentopdf': 'ghcr.io/alam00000/bentopdf:v2.8.6'}, 'catalog_images': {'bentopdf': 'localhost:5000/drill/pdf:1.0.1'}, 'live_compose_image_lines': ['image: ghcr.io/alam00000/bentopdf:v2.8.6'], 'docker_inspect': ['bentopdf ghcr.io/alam00000/bentopdf:v2.8.6 running=true restarts=0']}}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
{
|
||||
"leg": "B3",
|
||||
"app": "bentopdf",
|
||||
"during_backup": [
|
||||
{
|
||||
"i": 0,
|
||||
"http": "409",
|
||||
"reason": "busy",
|
||||
"error": "A frissítés most nem indítható: mentés/visszaállítás folyamatban. Próbáld újra, ha befejeződött."
|
||||
},
|
||||
{
|
||||
"i": 1,
|
||||
"http": "409",
|
||||
"reason": "busy",
|
||||
"error": "A frissítés most nem indítható: mentés/visszaállítás folyamatban. Próbáld újra, ha befejeződött."
|
||||
},
|
||||
{
|
||||
"i": 2,
|
||||
"http": "409",
|
||||
"reason": "busy",
|
||||
"error": "A frissítés most nem indítható: mentés/visszaállítás folyamatban. Próbáld újra, ha befejeződött."
|
||||
},
|
||||
{
|
||||
"i": 3,
|
||||
"http": "409",
|
||||
"reason": "busy",
|
||||
"error": "A frissítés most nem indítható: mentés/visszaállítás folyamatban. Próbáld újra, ha befejeződött."
|
||||
},
|
||||
{
|
||||
"i": 4,
|
||||
"http": "409",
|
||||
"reason": "busy",
|
||||
"error": "A frissítés most nem indítható: mentés/visszaállítás folyamatban. Próbáld újra, ha befejeződött."
|
||||
},
|
||||
{
|
||||
"i": 5,
|
||||
"http": "409",
|
||||
"reason": "busy",
|
||||
"error": "A frissítés most nem indítható: mentés/visszaállítás folyamatban. Próbáld újra, ha befejeződött."
|
||||
}
|
||||
],
|
||||
"after_backup": {
|
||||
"http": "202",
|
||||
"reason": null,
|
||||
"body": {
|
||||
"ok": true,
|
||||
"data": {
|
||||
"accepted": true,
|
||||
"completed": false
|
||||
},
|
||||
"message": "Frissítés elindult – az állapot a kártyán követhető"
|
||||
}
|
||||
},
|
||||
"the_update_that_got_through": {
|
||||
"final_phase": "failed",
|
||||
"update_error": "Az új verzió letöltése nem sikerült, ezért a frissítés elmaradt. Az alkalmazás a korábbi verzióval fut tovább.",
|
||||
"hold_reason": null,
|
||||
"seconds": 2.0,
|
||||
"observables": {
|
||||
"pinned_images": {
|
||||
"bentopdf": "ghcr.io/alam00000/bentopdf:v2.8.6"
|
||||
},
|
||||
"installed_images": {
|
||||
"bentopdf": "ghcr.io/alam00000/bentopdf:v2.8.6"
|
||||
},
|
||||
"catalog_images": {
|
||||
"bentopdf": "localhost:5000/drill/pdf:1.0.1"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: ghcr.io/alam00000/bentopdf:v2.8.6"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"bentopdf ghcr.io/alam00000/bentopdf:v2.8.6 running=true restarts=0"
|
||||
]
|
||||
}
|
||||
},
|
||||
"sentences": {
|
||||
"hu": [
|
||||
"BentoPDF — Felhom.eu Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Telepített alkalmazás nem fut: Glance (stopped) Rendszermonitor → Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások BentoPDF Fut Frissítés elérhető — ma Megnyitás ↗ Napló Exportálás Beállítások Az új verzió letöltése nem sikerült, ezért a frissítés elmaradt.",
|
||||
"Adatvédelmi fókuszú PDF eszköztár - a fájlok soha nem hagyják el a szervered ~100M RAM tools Pi kompatibilis Áthelyezés másik tárhelyre Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át.",
|
||||
"PDF összefűzés, szétválasztás és forgatás PDF konvertálás képekké és képekből Jelszóvédelem és titkosítás Vízjel hozzáadása dokumentumokhoz Minden feldolgozás a szerveren - a fájlok nem kerülnek külső szolgáltatóhoz Első lépések Nyisd meg a pdf.DOMAIN címet a böngészőben Válaszd ki a kívánt műveletet Töltsd fel a PDF fájlt és kattints a feldolgozásra Dokumentáció Hivatalos dokumentáció ↗"
|
||||
],
|
||||
"en": [
|
||||
"BentoPDF — Felhom.eu Launcher Dashboard Apps Storage Drives Network storage Backup Overview Remote backup Apps Restore Sharing Network sharing System monitor Debug Settings System Notifications Security and access 0.261.0 Magyar English Sign out ↗ An installed app is not running: Glance (stopped) System monitor → The hub connection is off — central monitoring is not running System monitor → ← Apps BentoPDF Running Update available — today Open ↗ Log Export Settings Az új verzió letöltése nem sikerült, ezért a frissítés elmaradt.",
|
||||
"Merge, split and rotate PDFs Convert PDFs to images and images to PDFs Password protection and encryption Add a watermark to a document Everything happens on your own server - no file goes to an outside service First steps Open pdf.DOMAIN in your browser Choose what you want to do Upload the PDF and start it Documentation Official documentation ↗"
|
||||
]
|
||||
}
|
||||
}
|
||||
+343
@@ -0,0 +1,343 @@
|
||||
{
|
||||
"leg": "B4-five",
|
||||
"apps": [
|
||||
"privatebin",
|
||||
"bentopdf",
|
||||
"wishlist",
|
||||
"uptime-kuma",
|
||||
"opengist"
|
||||
],
|
||||
"presses": {
|
||||
"wishlist": {
|
||||
"http": "202",
|
||||
"at_s": 0.359,
|
||||
"reason": null,
|
||||
"error": null
|
||||
},
|
||||
"uptime-kuma": {
|
||||
"http": "202",
|
||||
"at_s": 0.38,
|
||||
"reason": null,
|
||||
"error": null
|
||||
},
|
||||
"privatebin": {
|
||||
"http": "202",
|
||||
"at_s": 0.399,
|
||||
"reason": null,
|
||||
"error": null
|
||||
},
|
||||
"opengist": {
|
||||
"http": "202",
|
||||
"at_s": 0.446,
|
||||
"reason": null,
|
||||
"error": null
|
||||
},
|
||||
"bentopdf": {
|
||||
"http": "202",
|
||||
"at_s": 0.448,
|
||||
"reason": null,
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
"press_spread_s": 0.448,
|
||||
"max_concurrent_updates": 5,
|
||||
"samples": [
|
||||
{
|
||||
"t": 0.1,
|
||||
"state": {
|
||||
"privatebin": [
|
||||
true,
|
||||
"safety-dump"
|
||||
],
|
||||
"bentopdf": [
|
||||
true,
|
||||
"safety-dump"
|
||||
],
|
||||
"wishlist": [
|
||||
true,
|
||||
"pulling"
|
||||
],
|
||||
"uptime-kuma": [
|
||||
true,
|
||||
"pulling"
|
||||
],
|
||||
"opengist": [
|
||||
true,
|
||||
"pinning"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 5.3,
|
||||
"state": {
|
||||
"privatebin": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"bentopdf": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"wishlist": [
|
||||
true,
|
||||
"verifying"
|
||||
],
|
||||
"uptime-kuma": [
|
||||
true,
|
||||
"verifying"
|
||||
],
|
||||
"opengist": [
|
||||
true,
|
||||
"verifying"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 7.5,
|
||||
"state": {
|
||||
"privatebin": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"bentopdf": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"wishlist": [
|
||||
true,
|
||||
"verifying"
|
||||
],
|
||||
"uptime-kuma": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"opengist": [
|
||||
false,
|
||||
"done"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 9.6,
|
||||
"state": {
|
||||
"privatebin": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"bentopdf": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"wishlist": [
|
||||
true,
|
||||
"verifying"
|
||||
],
|
||||
"uptime-kuma": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"opengist": [
|
||||
false,
|
||||
"done"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 11.7,
|
||||
"state": {
|
||||
"privatebin": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"bentopdf": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"wishlist": [
|
||||
true,
|
||||
"verifying"
|
||||
],
|
||||
"uptime-kuma": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"opengist": [
|
||||
false,
|
||||
"done"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 15.8,
|
||||
"state": {
|
||||
"privatebin": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"bentopdf": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"wishlist": [
|
||||
true,
|
||||
"verifying"
|
||||
],
|
||||
"uptime-kuma": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"opengist": [
|
||||
false,
|
||||
"done"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 17.9,
|
||||
"state": {
|
||||
"privatebin": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"bentopdf": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"wishlist": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"uptime-kuma": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"opengist": [
|
||||
false,
|
||||
"done"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"memory_samples": [
|
||||
"Mem: 25898 970 6988 57 17996 24927\n",
|
||||
"Mem: 25898 1037 6855 57 18062 24860\n"
|
||||
],
|
||||
"final": {
|
||||
"privatebin": {
|
||||
"state": "running",
|
||||
"update_phase": "done",
|
||||
"update_error": null,
|
||||
"hold_reason": null,
|
||||
"observables": {
|
||||
"pinned_images": {
|
||||
"privatebin": "localhost:5000/drill/paste:2.0.1"
|
||||
},
|
||||
"installed_images": {
|
||||
"privatebin": "localhost:5000/drill/paste:2.0.1"
|
||||
},
|
||||
"catalog_images": {
|
||||
"privatebin": "localhost:5000/drill/paste:2.0.1"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: localhost:5000/drill/paste:2.0.1"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"privatebin localhost:5000/drill/paste:2.0.1 running=true restarts=0"
|
||||
]
|
||||
}
|
||||
},
|
||||
"bentopdf": {
|
||||
"state": "running",
|
||||
"update_phase": "done",
|
||||
"update_error": null,
|
||||
"hold_reason": null,
|
||||
"observables": {
|
||||
"pinned_images": {
|
||||
"bentopdf": "localhost:5000/drill/pdf:2.0.1"
|
||||
},
|
||||
"installed_images": {
|
||||
"bentopdf": "localhost:5000/drill/pdf:2.0.1"
|
||||
},
|
||||
"catalog_images": {
|
||||
"bentopdf": "localhost:5000/drill/pdf:2.0.1"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: localhost:5000/drill/pdf:2.0.1"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"bentopdf localhost:5000/drill/pdf:2.0.1 running=true restarts=0"
|
||||
]
|
||||
}
|
||||
},
|
||||
"wishlist": {
|
||||
"state": "running",
|
||||
"update_phase": "done",
|
||||
"update_error": null,
|
||||
"hold_reason": null,
|
||||
"observables": {
|
||||
"pinned_images": {
|
||||
"wishlist": "localhost:5000/drill/wishes:2.0.1"
|
||||
},
|
||||
"installed_images": {
|
||||
"wishlist": "localhost:5000/drill/wishes:2.0.1"
|
||||
},
|
||||
"catalog_images": {
|
||||
"wishlist": "localhost:5000/drill/wishes:2.0.1"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: localhost:5000/drill/wishes:2.0.1"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"wishlist localhost:5000/drill/wishes:2.0.1 running=true restarts=0"
|
||||
]
|
||||
}
|
||||
},
|
||||
"uptime-kuma": {
|
||||
"state": "running",
|
||||
"update_phase": "done",
|
||||
"update_error": null,
|
||||
"hold_reason": null,
|
||||
"observables": {
|
||||
"pinned_images": {
|
||||
"uptime-kuma": "localhost:5000/drill/status:2.0.1"
|
||||
},
|
||||
"installed_images": {
|
||||
"uptime-kuma": "localhost:5000/drill/status:2.0.1"
|
||||
},
|
||||
"catalog_images": {
|
||||
"uptime-kuma": "localhost:5000/drill/status:2.0.1"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: localhost:5000/drill/status:2.0.1"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"uptime-kuma localhost:5000/drill/status:2.0.1 running=true restarts=0"
|
||||
]
|
||||
}
|
||||
},
|
||||
"opengist": {
|
||||
"state": "running",
|
||||
"update_phase": "done",
|
||||
"update_error": null,
|
||||
"hold_reason": null,
|
||||
"observables": {
|
||||
"pinned_images": {
|
||||
"opengist": "localhost:5000/drill/gist:2.0.1"
|
||||
},
|
||||
"installed_images": {
|
||||
"opengist": "localhost:5000/drill/gist:2.0.1"
|
||||
},
|
||||
"catalog_images": {
|
||||
"opengist": "localhost:5000/drill/gist:2.0.1"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: localhost:5000/drill/gist:2.0.1"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"opengist localhost:5000/drill/gist:2.0.1 running=true restarts=0"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"elapsed_s": 28.4
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
22:06:29 ==== B4 [five]: pressing Update on ['privatebin', 'bentopdf', 'wishlist', 'uptime-kuma', 'opengist'] at once
|
||||
22:06:29 press wishlist +0.359s http=202 reason=None ::
|
||||
22:06:29 press uptime-kuma +0.380s http=202 reason=None ::
|
||||
22:06:29 press privatebin +0.399s http=202 reason=None ::
|
||||
22:06:30 press opengist +0.446s http=202 reason=None ::
|
||||
22:06:30 press bentopdf +0.448s http=202 reason=None ::
|
||||
22:06:30 all 5 presses issued within 0.448s
|
||||
22:06:30 + 0.1s updating=5 privatebin=safety-dump bentopdf=safety-dump wishlist=pulling uptime-kuma=pulling opengist=pinning
|
||||
22:06:41 + 11.7s updating=1 privatebin=done bentopdf=done wishlist=verifying uptime-kuma=done opengist=done
|
||||
22:06:47 MOST UPDATES IN FLIGHT AT ONCE: 5 of 5
|
||||
22:06:50 privatebin ended phase=done err=None hold=None pinned={'privatebin': 'localhost:5000/drill/paste:2.0.1'}
|
||||
22:06:52 bentopdf ended phase=done err=None hold=None pinned={'bentopdf': 'localhost:5000/drill/pdf:2.0.1'}
|
||||
22:06:54 wishlist ended phase=done err=None hold=None pinned={'wishlist': 'localhost:5000/drill/wishes:2.0.1'}
|
||||
22:06:56 uptime-kuma ended phase=done err=None hold=None pinned={'uptime-kuma': 'localhost:5000/drill/status:2.0.1'}
|
||||
22:06:58 opengist ended phase=done err=None hold=None pinned={'opengist': 'localhost:5000/drill/gist:2.0.1'}
|
||||
@@ -0,0 +1,116 @@
|
||||
{
|
||||
"leg": "B4-two",
|
||||
"apps": [
|
||||
"privatebin",
|
||||
"bentopdf"
|
||||
],
|
||||
"presses": {
|
||||
"privatebin": {
|
||||
"http": "202",
|
||||
"at_s": 0.268,
|
||||
"reason": null,
|
||||
"error": null
|
||||
},
|
||||
"bentopdf": {
|
||||
"http": "202",
|
||||
"at_s": 0.268,
|
||||
"reason": null,
|
||||
"error": null
|
||||
}
|
||||
},
|
||||
"press_spread_s": 0.268,
|
||||
"max_concurrent_updates": 2,
|
||||
"samples": [
|
||||
{
|
||||
"t": 0.0,
|
||||
"state": {
|
||||
"privatebin": [
|
||||
true,
|
||||
"safety-dump"
|
||||
],
|
||||
"bentopdf": [
|
||||
true,
|
||||
"safety-dump"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 4.6,
|
||||
"state": {
|
||||
"privatebin": [
|
||||
true,
|
||||
"verifying"
|
||||
],
|
||||
"bentopdf": [
|
||||
true,
|
||||
"verifying"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"t": 6.6,
|
||||
"state": {
|
||||
"privatebin": [
|
||||
false,
|
||||
"done"
|
||||
],
|
||||
"bentopdf": [
|
||||
false,
|
||||
"done"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"memory_samples": [
|
||||
"Mem: 25898 874 8080 56 16999 25023\n"
|
||||
],
|
||||
"final": {
|
||||
"privatebin": {
|
||||
"state": "running",
|
||||
"update_phase": "done",
|
||||
"update_error": null,
|
||||
"hold_reason": null,
|
||||
"observables": {
|
||||
"pinned_images": {
|
||||
"privatebin": "localhost:5000/drill/paste:2.0.1"
|
||||
},
|
||||
"installed_images": {
|
||||
"privatebin": "localhost:5000/drill/paste:2.0.1"
|
||||
},
|
||||
"catalog_images": {
|
||||
"privatebin": "localhost:5000/drill/paste:2.0.1"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: localhost:5000/drill/paste:2.0.1"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"privatebin localhost:5000/drill/paste:2.0.1 running=true restarts=0"
|
||||
]
|
||||
}
|
||||
},
|
||||
"bentopdf": {
|
||||
"state": "running",
|
||||
"update_phase": "done",
|
||||
"update_error": null,
|
||||
"hold_reason": null,
|
||||
"observables": {
|
||||
"pinned_images": {
|
||||
"bentopdf": "localhost:5000/drill/pdf:2.0.1"
|
||||
},
|
||||
"installed_images": {
|
||||
"bentopdf": "localhost:5000/drill/pdf:2.0.1"
|
||||
},
|
||||
"catalog_images": {
|
||||
"bentopdf": "localhost:5000/drill/pdf:2.0.1"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: localhost:5000/drill/pdf:2.0.1"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"bentopdf localhost:5000/drill/pdf:2.0.1 running=true restarts=0"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"elapsed_s": 10.8
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
=== the app's own recovery unit + its db dumps, with sizes and times
|
||||
2026-09-21 20:05 1536 /mnt/sys_drive/felhom-data/backups/primary/privatebin/volume-dumps/privatebin_privatebin_data.tar
|
||||
2026-09-21 20:06 288 /mnt/sys_drive/felhom-data/backups/primary/privatebin/compose/app.yaml
|
||||
2026-09-21 20:06 1050 /mnt/sys_drive/felhom-data/backups/primary/privatebin/manifest.json
|
||||
2026-09-21 20:06 1235 /mnt/sys_drive/felhom-data/backups/primary/privatebin/compose/docker-compose.yml
|
||||
2026-09-21 20:06 3125 /mnt/sys_drive/felhom-data/backups/primary/privatebin/compose/.felhom.yml
|
||||
=== any temp/partial names left behind
|
||||
=== the unit manifest, if there is one
|
||||
--- /mnt/sys_drive/felhom-data/backups/primary/privatebin/manifest.json
|
||||
{
|
||||
"schema_version": 2,
|
||||
"app_name": "privatebin",
|
||||
"display_name": "PrivateBin",
|
||||
"controller_version": "0.261.0",
|
||||
"created_at": "2026-09-21T20:06:16Z",
|
||||
"drive": "/mnt/sys_drive",
|
||||
"namespace_root": "/mnt/sys_drive/felhom-data",
|
||||
"image_pins": [
|
||||
"localhost:5000/drill/paste:2.0.1"
|
||||
],
|
||||
"secret_env_vars": null,
|
||||
"data_key_env_vars": null,
|
||||
"secret_source": "portable secrets (data keys, DB passwords, internal signing secrets) are IN this unit's compose/app.yaml (0600); internet-reachable admin logins are NOT, and come from the guest's app.yaml or are regenerated on restore",
|
||||
"config_files": [
|
||||
"docker-compose.yml",
|
||||
".felhom.yml",
|
||||
"app.yaml"
|
||||
],
|
||||
"db_dumps": [],
|
||||
=== ZERO-BYTE files under this app's backups (a half-write that still looks like a file)
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
=== the app's own recovery unit + its db dumps, with sizes and times
|
||||
2026-09-21 20:06 288 /mnt/sys_drive/felhom-data/backups/primary/privatebin/compose/app.yaml
|
||||
2026-09-21 20:06 1050 /mnt/sys_drive/felhom-data/backups/primary/privatebin/manifest.json
|
||||
2026-09-21 20:06 1235 /mnt/sys_drive/felhom-data/backups/primary/privatebin/compose/docker-compose.yml
|
||||
2026-09-21 20:06 3125 /mnt/sys_drive/felhom-data/backups/primary/privatebin/compose/.felhom.yml
|
||||
2026-09-21 20:07 1536 /mnt/sys_drive/felhom-data/backups/primary/privatebin/volume-dumps/privatebin_privatebin_data.tar
|
||||
=== any temp/partial names left behind
|
||||
=== the unit manifest, if there is one
|
||||
--- /mnt/sys_drive/felhom-data/backups/primary/privatebin/manifest.json
|
||||
{
|
||||
"schema_version": 2,
|
||||
"app_name": "privatebin",
|
||||
"display_name": "PrivateBin",
|
||||
"controller_version": "0.261.0",
|
||||
"created_at": "2026-09-21T20:06:16Z",
|
||||
"drive": "/mnt/sys_drive",
|
||||
"namespace_root": "/mnt/sys_drive/felhom-data",
|
||||
"image_pins": [
|
||||
"localhost:5000/drill/paste:2.0.1"
|
||||
],
|
||||
"secret_env_vars": null,
|
||||
"data_key_env_vars": null,
|
||||
"secret_source": "portable secrets (data keys, DB passwords, internal signing secrets) are IN this unit's compose/app.yaml (0600); internet-reachable admin logins are NOT, and come from the guest's app.yaml or are regenerated on restore",
|
||||
"config_files": [
|
||||
"docker-compose.yml",
|
||||
".felhom.yml",
|
||||
"app.yaml"
|
||||
],
|
||||
"db_dumps": [],
|
||||
=== ZERO-BYTE files under this app's backups (a half-write that still looks like a file)
|
||||
@@ -0,0 +1,17 @@
|
||||
22:07:23 ==== B5: a power cut during `backing-up` on privatebin
|
||||
22:07:24 [5] drill commit 97063c022be6: privatebin localhost:5000/drill/paste:2.0.1 -> localhost:5000/drill/paste:2.0.2 (push rc=0)
|
||||
22:07:28 [0] pending edge for the cut: installed={'privatebin': 'localhost:5000/drill/paste:2.0.1'} catalog={'privatebin': 'localhost:5000/drill/paste:2.0.1'}
|
||||
22:07:33 [0] pinned before = {'privatebin': 'localhost:5000/drill/paste:2.0.1'}
|
||||
22:07:33 [cut] Update -> 202 {'ok': True, 'data': {'accepted': True, 'completed': False}, 'message': 'Frissítés elindult – az állapot a kártyán követ
|
||||
22:07:33 + 0.027s phase=backing-up
|
||||
22:07:33 [cut] phase 'backing-up' OBSERVED at 2026-09-21T20:07:33.345Z — pulling the plug NOW
|
||||
22:07:42 [cut] `pct stop 9202` returned after 9.65s ::
|
||||
22:07:46 [boot] guest 9202 starting
|
||||
22:07:53 [boot] controller back: Up Less than a second (health: starting)
|
||||
22:08:15 [boot] recovery lines: 2026/09/21 20:07:53 appstop_marker.go:278: [WARN] [appstop] crash recovery: an app-data backup (volume dump) (op "volume-dump:privatebin") was interrupted and left 1 app(s) stopped — restarting them: [privatebin] | 2026/09/21 20:07:53 appstop_marker.go:297: [INFO] [appstop] crash recovery: restarted privatebin after the interrupted an app-data backup (volume dump) | 2026/09/21 20:07:53 update.go:959: [WARN] [stacks] update recovery: privatebin was interrupted in backing-up (started 2026-09-21T20
|
||||
22:08:19 [2] household sentence HU: ['PrivateBin — Felhom.eu Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások PrivateBin Fut Naprakész Megnyitás ↗ Napló Exportálás Beállítások A frissítés megszakadt, mert a vezérlő újraindult, mielőtt az új verzió elindult volna.', 'Titkosított szöveg megosztás - a szerver nem látja a tartalmat ~30M RAM security Pi kompatibilis Áthelyezés másik tárhelyre Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át.', 'Érzékeny szövegek biztonságos megosztása E2E titkosítás - a szerver nem fér hozzá a tartalomhoz Beállítható lejárati idő (5 perc - 1 év, vagy soha) Olvasás után automatikus törlés opció Jelszóvédelem a még nagyobb biztonságért Első lépések Nyisd meg a paste.DOMAIN címet a böngészőben Írd be a szöveget és kattints a Küldés gombra Oszd meg a generált linket - a titkosítási kulcs az URL-ben van Dokumentáció Hivatalos dokumentáció ↗']
|
||||
22:08:19 [2] household sentence EN: ['PrivateBin — Felhom.eu Launcher Dashboard Apps Storage Drives Network storage Backup Overview Remote backup Apps Restore Sharing Network sharing System monitor Debug Settings System Notifications Security and access 0.261.0 Magyar English Sign out ↗ The hub connection is off — central monitoring is not running System monitor → ← Apps PrivateBin Running Up to date Open ↗ Log Export Settings A frissítés megszakadt, mert a vezérlő újraindult, mielőtt az új verzió elindult volna.', 'Share sensitive text safely End-to-end encryption - the server cannot reach the content You choose how long it lasts (5 minutes to 1 year, or never) Optional: delete it automatically after it is read Password protection for extra safety First steps Open paste.DOMAIN in your browser Type your text and select Send Share the link you get - the encryption key is part of the URL Documentation Official documentation ↗']
|
||||
22:08:19 privatebin: seeded paste id=1a72111ee336aa1f
|
||||
22:08:20 privatebin: readback http=200 marker_present=True
|
||||
22:08:20 [3] the app works and holds data after the cut: True
|
||||
22:08:20 [4] pinned after = {'privatebin': 'localhost:5000/drill/paste:2.0.1'} (moved: False)
|
||||
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"leg": "B5-backing-up",
|
||||
"app": "privatebin",
|
||||
"phase_targeted": "backing-up",
|
||||
"observables_before": {
|
||||
"pinned_images": {
|
||||
"privatebin": "localhost:5000/drill/paste:2.0.1"
|
||||
},
|
||||
"installed_images": {
|
||||
"privatebin": "localhost:5000/drill/paste:2.0.1"
|
||||
},
|
||||
"catalog_images": {
|
||||
"privatebin": "localhost:5000/drill/paste:2.0.1"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: localhost:5000/drill/paste:2.0.1"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"privatebin localhost:5000/drill/paste:2.0.1 running=true restarts=0"
|
||||
]
|
||||
},
|
||||
"backup_artefacts_before": "=== the app's own recovery unit + its db dumps, with sizes and times\n2026-09-21 20:05 1536 /mnt/sys_drive/felhom-data/backups/primary/privatebin/volume-dumps/privatebin_privatebin_data.tar\n2026-09-21 20:06 288 /mnt/sys_drive/felhom-data/backups/primary/privatebin/compose/app.yaml\n2026-09-21 20:06 1050 /mnt/sys_drive/felhom-data/backups/primary/privatebin/manifest.json\n2026-09-21 20:06 1235 /mnt/sys_drive/felhom-data/backups/primary/privatebin/compose/docker-compose.yml\n2026-09-21 20:06 3125 /mnt/sys_drive/felhom-data/backups/primary/privatebin/compose/.felhom.yml\n=== any temp/partial names left behind\n=== the unit manifest, if there is one\n--- /mnt/sys_drive/felhom-data/backups/primary/privatebin/manifest.json\n{\n \"schema_version\": 2,\n \"app_name\": \"privatebin\",\n \"display_name\": \"PrivateBin\",\n \"controller_version\": \"0.261.0\",\n \"created_at\": \"2026-09-21T20:06:16Z\",\n \"drive\": \"/mnt/sys_drive\",\n \"namespace_root\": \"/mnt/sys_drive/felhom-data\",\n \"image_pins\": [\n \"localhost:5000/drill/paste:2.0.1\"\n ],\n \"secret_env_vars\": null,\n \"data_key_env_vars\": null,\n \"secret_source\": \"portable secrets (data keys, DB passwords, internal signing secrets) are IN this unit's compose/app.yaml (0600); internet-reachable admin logins are NOT, and come from the guest's app.yaml or are regenerated on restore\",\n \"config_files\": [\n \"docker-compose.yml\",\n \".felhom.yml\",\n \"app.yaml\"\n ],\n \"db_dumps\": [],\n=== ZERO-BYTE files under this app's backups (a half-write that still looks like a file)\n",
|
||||
"cut": {
|
||||
"pressed": true,
|
||||
"phases_seen": [
|
||||
{
|
||||
"t": 0.027,
|
||||
"phase": "backing-up"
|
||||
}
|
||||
],
|
||||
"cut_decided_at": "2026-09-21T20:07:33.345Z",
|
||||
"pct_stop_returned_after_s": 9.65,
|
||||
"instrument_limit": "the phase at the DECISION is observed; the phase at the FREEZE is inferred — pct stop is not instantaneous"
|
||||
},
|
||||
"after_boot": {
|
||||
"recovery_lines": "2026/09/21 20:07:53 appstop_marker.go:278: [WARN] [appstop] crash recovery: an app-data backup (volume dump) (op \"volume-dump:privatebin\") was interrupted and left 1 app(s) stopped — restarting them: [privatebin]\n2026/09/21 20:07:53 appstop_marker.go:297: [INFO] [appstop] crash recovery: restarted privatebin after the interrupted an app-data backup (volume dump)\n2026/09/21 20:07:53 update.go:959: [WARN] [stacks] update recovery: privatebin was interrupted in backing-up (started 2026-09-21T20:07:33Z) — nothing had moved; dropping it\n2026/09/21 20:07:53 restore_record_wiring.go:35: [INFO] [backup] restore record wired: /opt/docker/felhom-controller/data/restore-status.json (interrupted at startup: false)\n--- journal file:\nls: cannot access '/var/lib/docker/volumes/felhom-controller-data/_data/data/update-journal.json': No such file or directory\n",
|
||||
"state": "running",
|
||||
"update_phase": "failed",
|
||||
"update_error": "A frissítés megszakadt, mert a vezérlő újraindult, mielőtt az új verzió elindult volna. Az alkalmazás a korábbi verzióval fut tovább.",
|
||||
"hold_reason": null,
|
||||
"observables": {
|
||||
"pinned_images": {
|
||||
"privatebin": "localhost:5000/drill/paste:2.0.1"
|
||||
},
|
||||
"installed_images": {
|
||||
"privatebin": "localhost:5000/drill/paste:2.0.1"
|
||||
},
|
||||
"catalog_images": {
|
||||
"privatebin": "localhost:5000/drill/paste:2.0.1"
|
||||
},
|
||||
"live_compose_image_lines": [
|
||||
"image: localhost:5000/drill/paste:2.0.1"
|
||||
],
|
||||
"docker_inspect": [
|
||||
"privatebin localhost:5000/drill/paste:2.0.1 running=true restarts=0"
|
||||
]
|
||||
}
|
||||
},
|
||||
"backup_artefacts_after": "=== the app's own recovery unit + its db dumps, with sizes and times\n2026-09-21 20:06 288 /mnt/sys_drive/felhom-data/backups/primary/privatebin/compose/app.yaml\n2026-09-21 20:06 1050 /mnt/sys_drive/felhom-data/backups/primary/privatebin/manifest.json\n2026-09-21 20:06 1235 /mnt/sys_drive/felhom-data/backups/primary/privatebin/compose/docker-compose.yml\n2026-09-21 20:06 3125 /mnt/sys_drive/felhom-data/backups/primary/privatebin/compose/.felhom.yml\n2026-09-21 20:07 1536 /mnt/sys_drive/felhom-data/backups/primary/privatebin/volume-dumps/privatebin_privatebin_data.tar\n=== any temp/partial names left behind\n=== the unit manifest, if there is one\n--- /mnt/sys_drive/felhom-data/backups/primary/privatebin/manifest.json\n{\n \"schema_version\": 2,\n \"app_name\": \"privatebin\",\n \"display_name\": \"PrivateBin\",\n \"controller_version\": \"0.261.0\",\n \"created_at\": \"2026-09-21T20:06:16Z\",\n \"drive\": \"/mnt/sys_drive\",\n \"namespace_root\": \"/mnt/sys_drive/felhom-data\",\n \"image_pins\": [\n \"localhost:5000/drill/paste:2.0.1\"\n ],\n \"secret_env_vars\": null,\n \"data_key_env_vars\": null,\n \"secret_source\": \"portable secrets (data keys, DB passwords, internal signing secrets) are IN this unit's compose/app.yaml (0600); internet-reachable admin logins are NOT, and come from the guest's app.yaml or are regenerated on restore\",\n \"config_files\": [\n \"docker-compose.yml\",\n \".felhom.yml\",\n \"app.yaml\"\n ],\n \"db_dumps\": [],\n=== ZERO-BYTE files under this app's backups (a half-write that still looks like a file)\n",
|
||||
"sentences": {
|
||||
"hu": [
|
||||
"PrivateBin — Felhom.eu Indítópult Vezérlőpult Alkalmazások Tárhely Meghajtók Hálózati tárhely Biztonsági mentés Áttekintés Távoli mentés Alkalmazások Visszaállítás Megosztás Hálózati megosztás Rendszermonitor Debug Beállítások Rendszer Értesítések Biztonság és hozzáférés 0.261.0 Magyar English Kijelentkezés ↗ Hub kapcsolat kikapcsolva — a központi monitoring nem aktív Rendszermonitor → ← Alkalmazások PrivateBin Fut Naprakész Megnyitás ↗ Napló Exportálás Beállítások A frissítés megszakadt, mert a vezérlő újraindult, mielőtt az új verzió elindult volna.",
|
||||
"Titkosított szöveg megosztás - a szerver nem látja a tartalmat ~30M RAM security Pi kompatibilis Áthelyezés másik tárhelyre Ennek az alkalmazásnak az adatait másik csatlakoztatott tárhelyre helyezheted át.",
|
||||
"Érzékeny szövegek biztonságos megosztása E2E titkosítás - a szerver nem fér hozzá a tartalomhoz Beállítható lejárati idő (5 perc - 1 év, vagy soha) Olvasás után automatikus törlés opció Jelszóvédelem a még nagyobb biztonságért Első lépések Nyisd meg a paste.DOMAIN címet a böngészőben Írd be a szöveget és kattints a Küldés gombra Oszd meg a generált linket - a titkosítási kulcs az URL-ben van Dokumentáció Hivatalos dokumentáció ↗"
|
||||
],
|
||||
"en": [
|
||||
"PrivateBin — Felhom.eu Launcher Dashboard Apps Storage Drives Network storage Backup Overview Remote backup Apps Restore Sharing Network sharing System monitor Debug Settings System Notifications Security and access 0.261.0 Magyar English Sign out ↗ The hub connection is off — central monitoring is not running System monitor → ← Apps PrivateBin Running Up to date Open ↗ Log Export Settings A frissítés megszakadt, mert a vezérlő újraindult, mielőtt az új verzió elindult volna.",
|
||||
"Share sensitive text safely End-to-end encryption - the server cannot reach the content You choose how long it lasts (5 minutes to 1 year, or never) Optional: delete it automatically after it is read Password protection for extra safety First steps Open paste.DOMAIN in your browser Type your text and select Send Share the link you get - the encryption key is part of the URL Documentation Official documentation ↗"
|
||||
]
|
||||
},
|
||||
"app_usable_after": true,
|
||||
"pin_moved": false
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user