1474 Commits
Author SHA1 Message Date
Andras Bacsai 407c384b09 feat(backups): add configurable missing volume backup alerts
Check enabled volume backup schedules hourly and notify teams when no
execution occurs within the configured number of days. Expose the alert
threshold in the UI and API, with alerts disabled by default.

Route volume backup results and warnings to the appropriate Telegram
backup threads.
2026-10-07 09:37:35 +02:00
Andras Bacsai 116806df1b fix(compose): allow content files outside the resource directory again
Since aabd12737 a Compose bind volume with a content block had to stay
inside the resource directory. Services and Compose applications saved
in v4.3.23 with other paths, and Git Compose files with them, no longer
started or deployed.

Only admins and owners can edit Compose files, and they can already
write any host file with a plain bind mount, so the rule added no
security.

- Write content files to their host path again, as in v4.3.23
- Keep shell escaping and the safe-character checks on volume sources,
  so injection attempts are still rejected
2026-10-06 11:03:39 +02:00
Andras Bacsai 06b5a708b8 refactor(traffic): remove fallbacks for unreleased Sentinel builds
Every released Sentinel with traffic analytics (1.0.0 and later) has
the dashboard and series routes and the requests, bytes_in and app
fields, so the fallbacks for builds without them are not needed.

- Keep "no data" handling for Sentinel without traffic routes (0.0.x)
- Keep the resource-scope fallback, which Sentinel 1.0.1 needs
2026-10-06 10:29:04 +02:00
Andras Bacsai c5bcb31f73 fix(databases): import from the source the user selected
A staged upload always won over a server path, so an old upload that
was never imported could be restored instead of the chosen file.

- Record the selected source (checked server file or finished upload)
  and import only from it
- Delete a stale staged upload when another source is used
2026-10-06 10:29:04 +02:00
Andras Bacsai 58fb36544a fix(ui): lock the server-built domain state of applications and services
The domain rows of applications and previews, and the DNS validation
flags of application and service domains, were not locked. A user
could change them from the browser to skip the DNS check or store
fake DNS results.
2026-10-05 23:42:01 +02:00
Andras Bacsai 6cc6f1081d fix(deployments): advance the queue on all servers after a cancel
- A queued deployment on a deleted server no longer stops the queue;
  one deployment that fails to start does not stop the others
- Cancelling a deployment starts the queued deployments of the same
  application on all servers, as a finished deployment does
2026-10-05 23:10:52 +02:00
Andras Bacsai a331f91c49 fix(ui): validate secret manager settings and lock server-built lists
- SecretManagerLinks: validate settings when the integration token
  changes, and save only validated keys
- Service Domains: lock domainRows
- Server DockerImages: lock images
2026-10-05 21:23:29 +02:00
Andras Bacsai 379838251b fix(auth): use the resource team in database import, SQLite, and tokens
These paths used the session team after the resource was loaded. After a
team switch in another tab they used the wrong team or denied access.

- ImportForm: use the database team for the server, S3 storages, and
  the import
- SQLite ConnectApplication: list applications of the database team
- IntegrationTokenPolicy: check admin of the token team for view,
  update, and delete
2026-10-05 21:22:33 +02:00
Andras Bacsai 35c38b1dde fix(database): reject blocked starts before queueing operations
Check start prerequisites in database and deploy APIs and MCP controls.
Return HTTP 422 for blocked API starts and restarts. Show restart notices
and open UI logs only after an operation is queued.

Add API regression tests for blocked ClickHouse starts and restarts.
2026-10-05 14:59:59 +02:00
Andras Bacsai 88efc0716e fix: enforce resource team isolation and preserve ClickHouse data
Scope resource permissions and related records to the owning team across
team switches, cloning, deployments, and resource operations.

Merge ClickHouse volume mounts, preserve anonymous data volumes on stop,
and require adopting current data before starting or restarting.

Fix file storage deletion checks and add regression coverage.
2026-10-05 14:23:04 +02:00
Andras Bacsai 439ee6faea fix: restrict proxy content access and scope backup storage to owners
- Hide proxy configuration contents from users without update permission.
- Select and validate backup S3 storage against the resource's team.
- Keep scheduled branch syncs green while a conflict PR remains open.
- Document external mount permissions and Sentinel traffic limits.
- Add regression coverage for authorization and backup storage selection.
2026-10-05 13:20:19 +02:00
Andras Bacsai 4227ec6ebf fix(storage): require deploy permission for external host writes
Require deploy or root token access when creating managed mounts outside
resource directories or changing their file contents through the API.

Synchronize KeyDB and Dragonfly passwords with local and shared
REDIS_PASSWORD values while preserving secret manager resolution.
2026-10-05 12:03:37 +02:00
Andras Bacsai 2aead9c914 refactor(analytics): remove application traffic overview card 2026-10-05 11:28:03 +02:00
Andras Bacsai 554bd354bf fix: support host storage paths and clean up cancelled deployments
Allow validated host file and directory mounts outside resource directories
while protecting external paths from deletion.

Clean up cancelled deployment containers and clear tracked process IDs.
Preserve legacy KeyDB and Dragonfly password parsing, accept server backup
paths without extension restrictions, and allow Compose variable proxy ports.
2026-10-05 11:08:52 +02:00
Andras Bacsai 16a8c7999d feat(audit): log account, resource, and instance settings changes
Add audit events for account actions, server and application settings,
services, destinations, storage, backup schedules, and scheduled tasks.
Track changed fields and skip unchanged saves without logging file content.

Add regression coverage for audit events and changed-field detection.
2026-10-04 17:32:50 +02:00
Andras Bacsai 4c2253c2ad fix(previews): restrict PR loading and status updates to GitHub Apps
Add Application::isGithubAppSource(), which checks that the source is a
GithubApp, and use it in place of is_github_based() for pull request
status updates in ApplicationDeploymentJob and for the "Load pull
requests" action in the previews view.

Previews::load_prs now throws an error for non-GitHub App sources
instead of calling the GitHub API with a GitLab or other source. Also
adds a test checking that GitLab sources don't show or load GitHub pull
requests.
2026-10-04 12:29:48 +02:00
Andras Bacsai c3b32322eb fix(volume-backups): back off failed container and S3 recovery
Recovery attempts now back off exponentially, stop after 8 attempts, and
mark the execution as needing attention with a failure category. The
scheduler dispatches at most 100 recovery jobs per run, and a pending S3
cleanup no longer blocks new backups. S3 delete errors now raise with
their cause.
2026-10-03 21:25:11 +02:00
Andras BacsaiandClaude Opus 5.5 aa2592b17c fix(security): stop leaking locked shared variables and proxy secrets
- References to locked (shown once) shared variables keep their reference
  text in the UI, copy action, and API instead of the locked value.
- Remove the saved proxy configuration and validation logs from nested
  servers (for example destination.server) in API responses unless the
  token can read sensitive data.
- The services list API now applies removeSensitiveData to every service.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:01:00 +02:00
Andras BacsaiandClaude Opus 5.5 a5ec854e35 fix(services): resolve the old denoKV template key to deno-kv
A renamed template keeps its old key as an alias, so API callers and
services stored with the old key still find the template.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:00:52 +02:00
Andras Bacsai 50be16be8f revert(scheduler): go back to the cron scan and keep the reliability fixes
The missed daily backups on Coolify Cloud came from Redis evicting queue keys
(allkeys-lfu on a full instance), not from the scan of all schedules. The
next_run_at design (452259aac, 5c5740533) did not prevent that, and it added
stored state that must be kept in sync on every frequency, timezone, and server
change. It was not released, so remove it:
- revert next_run_at, its model hooks, the "Next run" UI, and the diagnostics
  changes; restore scheduled_job_states and the queue overlap lock
- a new migration removes the next_run_at columns and creates
  scheduled_job_states again where the removed migration already ran

Keep the changes that fix real problems:
- run the manager every minute from the scheduler with `scheduled:dispatch`,
  so a busy queue cannot delay it past the catch-up window
- load the server chain of scheduled backups with each chunk, which removes
  about 7,000 queries per run on Coolify Cloud
- catch Throwable for each schedule, so one broken schedule does not stop the
  others
- publish database and volume backups again when their queued job was not
  started after 60 minutes (for example, lost in Redis); log lost tasks and
  Docker cleanups as missed. A lost backup is not run again when a newer
  occurrence of the same schedule exists.
2026-10-02 08:05:18 +02:00
Andras Bacsai 0ed423aaac feat(webhooks): require 16 characters for new manual webhook secrets
New or changed manual webhook secrets (GitHub, GitLab, Bitbucket,
Gitea) must have at least 16 characters, in the Webhooks page and in
the application API. Stored secrets keep working: an unchanged value
passes, and an empty value still disables the webhook. Each secret
field gets a Generate button that fills a random 40-character secret;
the user saves it after adding it to the Git provider.
2026-10-01 20:31:00 +02:00
Andras Bacsai 7a4709da69 fix(ui): scope env refreshes, block duplicate container names, use a confirm modal
- refreshEnvs is now sent only to the environment variable list, and
  rows no longer listen to it, so adding or removing variables cannot
  queue an update for a removed row ("Snapshot missing"). Locking a row
  refreshes that row directly; the compose editor gets its own event.
- saveCustomName() no longer saves a custom container name that another
  application on the server already uses; it restores the stored value.
- Cancelling a GitHub runner uses the Coolify confirmation modal instead
  of a native browser dialog.
2026-10-01 20:31:00 +02:00
Andras Bacsai 3379f85c0a fix(security): authorize import file checks and Vultr status refresh
ImportForm::checkFile() and checkS3File() ran without authorization, so
a member with view access could check whether paths exist on the server
or probe S3. They now require update access, like the restore actions.
Server\Show::checkVultrInstanceStatus() now requires view access, like
the Hetzner and DigitalOcean checks.
2026-10-01 20:31:00 +02:00
Andras Bacsai fed15ff1b0 fix(backups): back up every SQLite file unless the user picks some
New SQLite backup schedules copied the database's file list at creation,
so files added later were never backed up. Schedules created in the UI
or the API without an explicit file list now store none, and the backup
job resolves the current files at run time. An explicit choice is still
stored. Existing schedules are not changed.
2026-10-01 20:31:00 +02:00
Andras Bacsai d1cf3e6ef1 fix(secrets): keep the secret manager source when cloning resources
Cloning an application, database, service or environment did not copy
the secret manager link, so variables with remote secret references
failed on the next deployment with "no secret manager source is
configured".

Clones now get a copy of the link when the integration token belongs to
the clone's team. Server transfer does not export integration tokens or
links; the export now warns which resources need the token and link
again on the target instance.
2026-10-01 07:16:45 +02:00
Andras Bacsai 8d7469230c fix(secrets): skip remote secret names that are not valid variable keys
Import all stopped at the first remote secret whose name is not a valid
environment variable key (for example db-password in Vault or
Infisical). The key setter threw, so the import was partial, no audit
entry was written, the variable list did not refresh, and the user saw
only a generic error.

Invalid names are now skipped and listed in the result toast and the
audit entry, and the rest are imported. Browse marks invalid names and
offers no add action for them, and adding one shows a clear error. The
refreshEnvs event now goes only to the environment variable list.
2026-10-01 07:16:45 +02:00
Andras Bacsai 7183e52779 fix(deploy): never force start the same deployment twice
Force start now uses the locked start path. It still skips the concurrency
limits, but a deployment that is no longer queued is not started again, and
the deployment page says so.
2026-09-30 19:23:35 +02:00
Andras Bacsai 8b007edceb fix(database): skip MySQL system databases when restoring all databases
An all-databases MySQL/MariaDB restore also restored the mysql system
database, so after the next restart the root and app passwords were the
source ones and the MariaDB health check failed. The restore now skips mysql,
sys, performance_schema and information_schema by default. The new option
"Restore users and privileges" (restore_mysql_users in the API) keeps the
old behaviour.
2026-09-30 18:59:49 +02:00
Andras Bacsai 5921fb2958 fix(compose): keep name-only declaration for pre-existing volumes
Before Coolify kept `driver`, `driver_opts` and `labels` of renamed
Compose volumes, Docker created those volumes without them. Applying the
options now would make Docker Compose ask to recreate the volumes on
every deployment.

- Add `ignores_compose_driver_options` column to local_persistent_volumes
  and mark all volumes that exist at upgrade time
- Parsers now use composeRenamedVolumeDeclarationFor() so flagged volumes
  keep the old name-only top-level declaration
- Legacy application parsers (v1/v2) always emit name-only declarations
- Reset the flag when a volume is replicated, since a clone is a new
  Docker volume
- Include the flag in server transfer export/import; bundles without it
  default to true
- Storages UI shows which volumes ignore driver options and allows
  deleting them so the options apply on recreation
- Add tests for parsers, cloning and server transfer
2026-09-30 14:31:33 +02:00
Andras Bacsai 9e7cb60b0d feat: add import restore options, OAuth confirmation, and fixes
- database import: add keep_owners (restore PostgreSQL owners and
  privileges) and sqlite_database (pick the target SQLite file) to the
  API, OpenAPI spec, and import form
- oauth: require password confirmation and support team selection on
  OAuth login
- remote process: send team broadcast events to the team of the server
  that ran the process instead of the causer's user id
- servers API: handle the legacy build server setting
- compose: support parent bind paths and volume driver options
- dns: improve record hints and provider management
- notifications: fix throttling and missing backup notifications
- ui: rework the confirmation modal
- add feature and unit tests for the new behavior
2026-09-30 13:35:22 +02:00
Andras Bacsai c70463fb9f fix(env): hide hardcoded compose values from users who cannot edit
Hardcoded variables parsed from docker-compose are now redacted in the
environment variable list unless the user can update the resource. The
value is nulled server-side and flagged with is_value_hidden, so it no
longer reaches the Livewire snapshot or rendered HTML.

ShowHardcoded gets a locked isValueHidden property. When it is set, the
copy button is hidden and the details modal shows "Hidden (only admins
can view)" in place of the value. Previously the view checked member
role only.

Add tests covering redaction for members and visibility for owners.
2026-09-30 11:31:29 +02:00
Andras Bacsai e2c826a976 fix: run SSH wrapper via sh and scope secret tokens to resource team
SSH: sshd runs the remote command with the user's login shell, which
can be fish or csh/tcsh. Those shells cannot parse POSIX `if ... fi`, so
the bash/sh fallback wrapper now runs inside `sh -c "..."`. Unit tests
run the wrapper under every login shell that is installed and check that
it stays one simple sh command.

Secret managers: the SecretManagerLinks component now lists and resolves
tokens by the resource's team instead of the session team. A team switch
in another tab can no longer link a token from a different team.
SecretManagerLink::fetchSecrets() also throws when the linked token does
not belong to the resource's team, so no request is sent with it.

Also adds a lesson note: remove the SSH mux file before live SSH or
login-shell tests.
2026-09-30 11:05:42 +02:00
Andras Bacsai 6a59008cda fix(dns): lock DNS modal state and unquote modal confirmation args
Mark DNS proposals, conflicts and server IP properties as #[Locked] so
clients cannot tamper with them. Re-resolve the zone per hostname,
lowercase hostnames, validate record content server-side and use the
resource's team id for DNS lookups and jobs.

Move modal-confirmation submitAction parsing into a tested JS helper
that strips quotes and keeps commas, parentheses and escaped quotes
inside quoted arguments. Pass the hostname to replaceManagedDnsRecord
through it instead of Js::from.

Rework member-permission tests to open the real DNS dialog rather than
setting locked properties directly.
2026-09-30 10:23:51 +02:00
Andras Bacsai 2b5bffe37d fix(service): delete resources from Coolify when server is unreachable
DeleteResourceJob no longer restores a service and rethrows when remote
cleanup fails. The resource is always removed from Coolify, and the team
gets a notification that Docker resources may remain on the server. No
notification is sent when the server itself was already deleted.

removeSubresourceContainer now returns false instead of throwing when the
server is not functional. Deleting a service application or database then
shows a warning that the container is removed on the next service start.

StopService handles a missing destination server, environment, project,
or team without throwing. The danger zone warning now shows for any
resource whose server is unreachable, not only services.
2026-09-30 08:02:02 +02:00
Andras Bacsai 680f4e5d29 fix: use GitLab API v4 for OAuth and gate env copy by update policy
Socialite's GitLab provider fetches the user from /api/v3/user, which
GitLab removed in 11.0 (410 Gone). Add App\Auth\GitlabProvider that
reads /api/v4/user with a bearer token and register it in the
socialite helper, for both gitlab.com and self-hosted base URLs.

Environment variable copyValue now checks the 'update' policy on the
variable instead of the member role of the current team, so members
can no longer copy values after switching to a team they own. Remove
the redundant member check from ShowHardcoded.

Add tests for the GitLab v4 user lookup and the team-switch copy case.
2026-09-29 22:36:56 +02:00
Andras Bacsai 49086c82a8 fix(multi-server): block additional servers with a mismatched proxy (#12053) 2026-09-29 11:10:22 +02:00
Andras BacsaiandClaude Opus 5.5 3452faec87 fix(livewire): bind team broadcast listeners to the mounted team
Livewire rebuilds listener names from the session team on every event
call. After a team switch in another tab, open pages still listen on the
old team channel, so events failed with EventHandlerDoesNotExist (500),
or ran handlers that load resources scoped to the new team (404).

Store the team at mount and build team channel listeners from it. Events
for a team that is no longer the session team are ignored.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 10:31:32 +02:00
Andras Bacsai 452259aacc feat(scheduler): dispatch scheduled jobs by their stored next run time
The scheduled job manager checked every enabled schedule each minute and
evaluated its cron expression in PHP. On Coolify Cloud one run took more than
60 seconds, so runs overlapped and minutes were skipped.

Each schedule now stores its next due time:
- next_run_at on scheduled database backups, scheduled tasks, and volume
  backups, and docker_cleanup_next_run_at on server settings (UTC, indexed)
- model hooks calculate it when a schedule is created or its frequency or
  enabled flag changes; a server timezone change or a move to another server
  calculates it again for the affected schedules

The `scheduled:dispatch` command runs the manager every minute from the
scheduler, not from a queue. It selects only due rows and claims each
occurrence with an atomic update of next_run_at, together with its delivery
record, so parallel runs on several nodes never dispatch it twice.
- database and volume backups run once when they are late; tasks and Docker
  cleanups more than 10 minutes late are logged as missed
- a local time that occurs two times when the clocks go back runs once
- the migration continues each schedule after its last recorded occurrence and
  drops scheduled_job_states

Show the next run, in the server timezone, on the database backup, scheduled
task, volume backup, and Docker cleanup pages, or "Invalid frequency" when the
frequency cannot be calculated. scheduled:diagnostics now lists overdue
schedules and open deliveries.

Remove workarounds that are no longer needed: the queue overlap lock and its
stale-lock repair, the catch-up window, and debug-only logs.
2026-09-29 10:07:23 +02:00
Andras Bacsai af983a29d8 refactor(docker): identify containers by resource UUID instead of numeric id
Container ownership labels now use UUIDs: coolify.applicationUuid,
coolify.serviceUuid, coolify.service.subUuid and coolify.databaseUuid.
Numeric ids differ between Coolify instances, so after a server transfer
the target could not find existing containers: redeploys left the old
container running and service statuses were wrong.

Containers are matched by UUID label first, then by the compose project
(Swarm: stack namespace), which is the resource UUID since July 2024, and
last by the older numeric id label. Existing containers keep working
without a redeploy and get the UUID labels on the next deploy or restart.
2026-09-29 09:57:56 +02:00
Andras BacsaiandClaude Opus 5.5 7bd8baa7f0 fix(multi-server): make applications on several servers consistent
- Stop the application on every server, also when one server is not functional.
- Write file mounts on every server of the application, not only on the main server.
- Deploy the exact commit, rollback flag and image tag of the main deployment to the
  additional servers, so a rollback reverts all servers.
- Pull the pushed image on additional servers for inline Dockerfile applications
  instead of building it again on each server.
- Fail multi-server deployments without a Docker image name, also from webhooks and the API.
- Send one deployment notification after all servers finish (parent_deployment_uuid).
- Block persistent volumes on multi-server applications (API, Livewire, SQLite connect)
  and warn in the deployment log for existing applications that have both.
- Remove dead code in push_to_docker_registry().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 15:38:57 +02:00
Andras BacsaiandClaude Opus 5.5 739d9e9d23 fix(permissions): show logs and task output only to admins and read:sensitive tokens
Container logs and scheduled-task commands and output are now shown only to
users who can edit the resource. The API returns them only to tokens with
the read:sensitive or root ability, the same as the MCP tools.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 13:20:59 +02:00
Andras BacsaiandClaude Opus 5.5 5ab6e1b245 fix(storage): show file mount content only to users who can edit the resource
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 12:53:39 +02:00
Andras BacsaiandClaude Opus 5.5 1e910394fd fix(compose): show compose content only to users who can edit the resource
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 12:30:47 +02:00
Andras BacsaiandClaude Opus 5.5 350d8a6571 fix(databases): refresh database form values consistently
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 12:07:02 +02:00
Andras BacsaiandClaude Opus 5.5 e778d99e9d feat(logs): render runtime logs in a virtualized list
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:09:13 +02:00
Andras BacsaiandClaude Opus 5.5 d59bcf5ae3 fix(logs): show container lookup errors and reload containers after status checks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 11:09:13 +02:00
Andras BacsaiandClaude Opus 5.5 f09de9c359 feat(ui): link healthcheck status row to the healthcheck page
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 10:47:01 +02:00
Andras BacsaiandClaude Opus 5.5 178e6665f8 fix: restore security and broken-feature fixes lost in the revert
Security:
- Settings email: authorize the SMTP and Resend toggles.
- Server navbar: show Sentinel status only to users who can view Sentinel.
- GitHub source: permission gates on Sync name and Delete.

Broken features:
- Profile: avatar upload (missing import and file uploads trait).
- API tokens: keep the "Never" expiry choice.
- Environment variables: show validation errors instead of a generic error.
- Servers API: read the server uuid from the route in domains_by_server,
  so ?uuid=<application> filters instead of returning 404.
- Cloud servers: Power On for stopped Hetzner, DigitalOcean and Vultr servers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 08:40:01 +02:00
Andras BacsaiandClaude Opus 5.5 a6a3b1d26b revert: undo the app changes from the test clean-up
Revert the app code changes of d666a0a95, 69eeb2f62, 1e66c9f9b and
a32082b03, so login, registration, OAuth, team danger zone, service
templates, and the other touched files are back to their earlier state.
Keep the removal of unused components and markup-only tests (d666a0a95
also contained those deletions by mistake).

StartupExecutionCleanupTest now fakes the template CDN with the current
file content, so app:init in the test no longer empties
templates/service-templates-latest.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 08:06:34 +02:00
Andras BacsaiandClaude Opus 5.5 a32082b03f fix: regressions found by the test suite
- TrustHosts: validate the Host header also when the FQDN cache is cold.
- Settings email: authorize the SMTP and Resend toggles.
- Server navbar: show Sentinel status only to users who can view Sentinel.
- GitHub source: restore permission gates on Sync name and Delete, and the
  Test connection button.
- Profile: make avatar upload work (missing import and file uploads trait).
- API tokens: keep the "Never" expiry choice.
- Environment variables: show validation errors instead of a generic error.
- Servers API: read the server uuid from the route in domains_by_server.
- Configuration diff: report all settings when no earlier snapshot exists.
- Cloud servers: restore Power On for stopped servers; do not repeat the
  provider error prefix.
- HasSecretManager: do not stop later deleting listeners.
- GitLab webhook: use new_public_id() like the other webhooks.
- Dev: show the Lima VM start command on the localhost server page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 00:18:09 +02:00