Repository navigation
Commit 003cc1b
authored
feat: installer hardening flags and one source for pinned versions (#49)
feat: installer hardening flags and one source for pinned versions
Installer hardening (opt-in, Linux only):
- install-agenthits.sh accepts --harden, --ufw, --ssh-keys-only and
--fail2ban, plus a harden mode for a host that is already installed.
The same switches are read as HARDEN_SSH, HARDEN_UFW and HARDEN_FAIL2BAN,
so the macOS (OrbStack machine) and Windows (WSL) launchers pass them on.
- SSH keys-only writes the main sshd_config, because the Security tab reads
that file and not drop-ins. It refuses to run without a valid root key and
skips hosts that have no sshd.
- UFW denies incoming traffic except SSH, 80, 443 (tcp, and udp for HTTP/3)
and keeps forwarding enabled for Docker. Fail2Ban runs the sshd jail in
aggressive mode from /etc/fail2ban/jail.local. UFW and Fail2Ban are
Debian/Ubuntu only and are skipped elsewhere.
Panel-driven server setup:
- DOKPLOY_SERVER_HARDENING (ssh, ufw, fail2ban or all) applies the same
hardening as the last step of Setup Server. It is off by default. The
installer sets it from its flags.
One source for component versions:
- packages/server/src/setup/component-versions.ts (PINNED_VERSIONS) pins
Node 24.21.0, Docker 29.8.2, Traefik 3.7.14, Postgres 18.6, Redis 8.10.2,
Nixpacks 1.41.0, Railpack 0.40.1, pack 0.40.9 and RClone 1.75.1.
- The installer defaults, remote server setup, both Dockerfiles, CI, and the
application Railpack picker read these values. component-versions-sync
fails when a file drifts from the object.
- Remote setup installs the pinned RClone release archive. The Ubuntu 26.04
Docker repin to 29.4.2 is gone, because 29.8.2 is published for noble and
resolute on amd64 and arm64.
- Setup Server still installs only what is missing. A Components button on
each server compares installed and pinned versions and updates Docker,
Traefik, RClone, Nixpacks, Railpack and pack. Docker updates restart the
daemon and Traefik is recreated; the dialog warns about both.
- The panel installer installs Nixpacks, Railpack, pack and RClone on the
host at the pinned versions.
Fix:
- Nixpacks and Railpack installed through sudo ignored the pinned version,
because sudo resets the environment. The version is passed with env now.
Docs: docs/server-hardening.md (flags, rollback, removing a server) and
docs/server-components-update.md (pins, update behaviour, limits).
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
docs: describe the panel machine generically in the hardening runbook
---
feat(db): default Railpack version for new applications to 0.40.1
New applications now store Railpack 0.40.1 as their default version, matching
the pinned version the installers and the remote setup use. Migration 0207
only changes the column default: existing applications keep the version they
have stored.
Checked on a throwaway Postgres 18.6 instance: a row inserted before the
migration keeps 0.15.4, and a row inserted after it without the column gets
0.40.1. drizzle-kit reports no further schema changes.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
docs: update the hardening runbook to the current Traefik pin
The runbook still named Traefik 3.7.5 and a test file that was replaced
by component-versions-sync.test.ts. Its update example also set
TRAEFIK_VERSION=3.7.5 on the panel, which would make new servers install
the old Traefik instead of the pinned 3.7.14. The example now only adds
DOKPLOY_SERVER_HARDENING; the installer sets TRAEFIK_VERSION from
TRAEFIK_IMAGE on its own.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
test: give the subprocess settings tests a 30s timeout
Two tests in the settings suite spawn bash or sh against fake docker and
curl scripts. They pass locally in about 3s, but on the CI runner, which
also runs real Docker builds in parallel, they exceeded vitest's 5s
default (pr-check run 37777323548). Raise the timeout only for the tests
that spawn processes. Most of the diff is biome reindenting the it()
calls whose timeout argument moved to its own line.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
fix(update): roll back a failed panel self-update
The panel self-update passed no failure action, so Swarm's default (pause)
applied: when the new task failed, the panel stayed down with no return to
the previous image. Both self-update commands now pass
--update-failure-action rollback, and state --update-order stop-first
explicitly.
Stop-first is deliberate. The panel runs drizzle migrations when its
container starts (apps/dokploy/migration.ts, run before server.ts), so an
overlapping old and new task would migrate and serve the same schema at
once. The image is pulled before the update, so the gap is only container
start time.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
feat(update): pre-download components before the update changes anything
The Components update removed the running Traefik container before the new
image was local, and the Docker step restarted the daemon before anything
else was fetched. Now one gate runs before every step: it pulls the pinned
Traefik image, fetches the get.docker.com script and, on apt hosts,
downloads the pinned Docker packages. Any failure aborts with "Nothing was
changed" while every running service is still untouched.
Traefik is swapped without removing the running container first. The old
container is stopped and renamed, the new one is started and connected to
the network, and the old one is removed only after the new one has been
running for TRAEFIK_SETTLE_SECONDS (10 by default). If the new container
fails to start or is not running, the old one is renamed back, started, and
the script exits non-zero. ERR and HUP/INT/TERM traps cover a failed run or
network connect and an SSH session that drops mid-swap.
Docker keeps its daemon restart. The Docker documentation scopes live-restore
to standalone containers and states that it does not apply to Swarm services,
which are the workload on these hosts, so daemon.json is not changed.
The "recreates the Traefik container" test asserted the remove-first step and
now asserts the rename. New tests run the generated script against fake
docker, apt-get, apt-cache, curl and sudo commands to check the order and the
restore paths.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
feat(update): read the panel update outcome from swarm
update.sh ran an attached docker service update. Attached mode has no
timeout, and it reports a completed rollback as success, so a panel update
that rolled back looked like a success. The update is now submitted with
--detach, and the script waits until the update is registered (the service
Version.Index has moved) before it reads UpdateStatus. It fails on a paused
or rolled-back state, and when the new task is not healthy within
AGENTHITS_HEALTH_TIMEOUT (240 seconds by default).
The update-script tests fake the service index, the update state and the
container health. A new case covers a rollback.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
feat(update): update the panel host stack in dependency order
install-agenthits.sh update replaced only the panel image. It now runs the
whole host stack, after one pre-download gate that pulls every pinned image
(Postgres, Redis, Traefik, the panel) and fetches update.sh before anything
is changed. Then, in order:
1. Postgres backup: pg_dumpall to AGENTHITS_BACKUP_DIR, kept on the host.
It runs before any swap, so a failed backup changes nothing.
2. Redis: stop-first swarm update with rollback on failure; waits for PONG.
3. Traefik: the running container is stopped and renamed, the new one is
started and joined to dokploy-network, and it must still be running after
AGENTHITS_TRAEFIK_SETTLE seconds. If it is not, the old container is put
back. The old container stays stopped as dokploy-traefik-previous with
restart=no, so a daemon restart cannot start it next to the new one.
4. Postgres: stop-first swarm update, which the shared data volume requires;
waits for pg_isready.
5. Panel: update.sh, which waits for health and rolls back on failure.
6. Docker Engine, only with DOCKER_ENGINE_UPGRADE=1: the packages are
downloaded in the gate and installed with --no-download after the rest is
healthy. The daemon restart is the one disruption that remains.
Order: Redis and Traefik fail before the database changes. Postgres follows
its backup and comes before the panel, because the new panel migrates the
database when it starts. The panel is last, so its dependencies are on their
pinned versions and healthy before its update runs.
The fresh install now calls run_dokploy_traefik for its Traefik container
with the same flags, so both paths share one definition.
install-agenthits-update.test.ts runs the installer in update mode against
fake docker, apt, curl, id and uname. It checks the order, that a failed
pre-download or backup changes nothing, that a rolled-back Redis stops the
update, and that a Traefik that does not start is restored.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
docs: describe the update order, rollback and backups
agenthits-install.md now lists the operator update steps in order, the
pre-download gate, the Postgres backup location, the Traefik keep-until-
verified swap, the optional Docker Engine upgrade and the manual rollback
commands. server-components-update.md states the pre-download gate, the
Traefik restore behaviour, why live-restore is not used for swarm services,
and that pre-downloading Docker packages needs apt. server-hardening.md
notes that the update path leaves SSH, UFW and Fail2Ban unchanged.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
refactor(update): name the new operator settings after the panel
The settings added for the operator update (backup directory, health timeout
and interval, Traefik settle time) now use the DOKPLOY_ prefix the installer
already uses for its own settings. Their messages no longer carry the fork
name, and the default backup directory is /var/backups/dokploy.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
fix(update): keep the Postgres backup readable by root only
pg_dumpall writes the data and the role password hashes. The backup was
created with the default umask, so the file could be world-readable. The
backup directory is now created 0700 and the dump runs under umask 077, so
the file is 0600. The installer test checks the mode of the file it writes.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
fix(update): refuse a Postgres major version change before anything runs
The Postgres swap keeps the existing volume mount, so a new major version
does not read the old data directory. The PostgreSQL 18 image would start a
new, empty cluster while the panel reconnects to it. The update now compares
the running and pinned major versions before the pre-download gate and stops
with "Nothing was changed" when they differ. The major version check reuses
get_postgres_major_version, which now takes an optional image argument.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
fix(update): set the backup directory mode with an explicit chmod
mkdir -p -m applies the mode only to the deepest directory, which shellcheck
flags as SC2174. The directory is created with mkdir -p and then chmod 700,
which is the same result without the ambiguity.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
fix(update): pin Docker in update.sh to the shared version
update.sh installed Docker 28.5.0 on a host without Docker, while the
installer and the component pins use 29.8.2. Read the version from
DOCKER_VERSION with the same default as the installer, and extend the
version sync test to cover update.sh.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
feat(update): roll back the panel host Docker engine upgrade
The Docker Engine upgrade stays off unless DOCKER_ENGINE_UPGRADE=1 is set in
the environment of the update command; the macOS and WSL launchers now pass
it on. Before the Engine is touched, the installed packages the upgrade
replaces are saved under DOKPLOY_BACKUP_DIR/docker-rollback-<UTC>/ (directory
0700, files 0600). After the upgrade the stack must report the pinned version
with an active swarm and healthy panel, Postgres, Redis and Traefik; if not,
the saved packages are installed back and the stack is checked again. When
the rollback fails too, the commands to finish it by hand are printed.
The Docker apt source is compared with VERSION_CODENAME before anything
changes, and a mismatch stops the update without rewriting the source.
Traefik is recreated with docker create, connected to dokploy-network and to
every other network of the old container (bridge excluded), and only then
started. /etc/dokploy is 755 instead of 777.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
feat(servers): install the pinned Docker from apt with rollback
The component update installs the pinned Docker from the packages downloaded
before any step runs, with apt-get install --no-download, instead of running
get.docker.com a second time. get.docker.com is still fetched only on hosts
without apt-get, where nothing can be pre-downloaded or rolled back.
Before anything changes, the Docker apt source is compared with
VERSION_CODENAME. When it names another release only the Docker step is
skipped, with a message, and the other components still run. When no Docker
source exists and apt does not offer the pinned version, Docker's repository
is added for ubuntu and debian; an existing source is never rewritten.
The packages that the upgrade replaces are saved first, as in the panel host
installer. If the daemon does not report the pinned version, or Traefik is not
running afterwards, the saved packages are installed back, Traefik is started
and the step fails.
Traefik is created with docker create, connected to dokploy-network and to
every other network of the old container (read on the host; bridge excluded),
and only then started. The old container is stopped just before the new one
starts, so the time without a running Traefik stays short.
The Docker step is opt-in: filterDockerUpgrade removes docker from the list
unless upgradeDocker is set, and logs one line saying so.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
feat(servers): make the Docker Engine upgrade opt-in in the Components dialog
The component update takes upgradeDocker (default false). The Components
dialog shows an unchecked "Upgrade Docker Engine (restarts all containers on
this server)" checkbox whenever Docker is among the outdated components, and
sends its value with the update. Without it, Docker is skipped and the update
says so in its log.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
docs: document Docker engine upgrade and rollback
Explains that the panel host upgrade of the Docker Engine needs
DOCKER_ENGINE_UPGRADE=1 in the environment of the update command, what is
saved for rollback and where, the checks after the upgrade, and the commands
to roll back by hand. Describes the new order in which Traefik is replaced.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
fix(update): set the Traefik restart policy before the swap stops the old container
Before the replaced Traefik container is stopped, its restart policy is set to
no, so a daemon restart cannot start it next to the new container. When the
old container is put back, its restart policy is set to always before it is
started. The panel host installer and the remote server update follow the
same order.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
fix(servers): skip only the Docker step on Docker-specific pre-download failures
A Docker-specific failure skips the Docker step with one message and lets the
other components run: a Docker apt source for another release, no pinned
candidate after the repository check, an unsupported distribution, a failure
to save the rollback packages (including hosts without docker-ce), and a host
without apt-get. Every other pre-download failure, such as the Traefik image,
the apt lists or the Docker packages, still aborts the whole update before any
change. get.docker.com is no longer run; a host without apt-get skips Docker
instead.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
fix(update): pass the same settings through the WSL launcher as through macOS
The WSL launcher forwarded fewer settings than the macOS launcher, so
DOKPLOY_BACKUP_DIR, DOKPLOY_HEALTH_TIMEOUT, DOKPLOY_HEALTH_INTERVAL,
DOKPLOY_TRAEFIK_SETTLE and AGENTHITS_PULL_RETRY_DELAY set on Windows never
reached the installer. They are added to $PassthroughVars. The macOS list
already had every setting. A test keeps the two lists equal.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
fix(servers): leave the host as it was when a later Docker step fails
When a run adds a Docker apt repository and a later Docker step fails, the key
and source file that this run wrote are removed. The steps covered are the key
download, the candidate check after the update, the download of the rollback
packages, and the download of the new packages. The first three skip only
Docker, and the other components still run. A failed download of the new
packages still aborts the update, now without the repository left behind.
A key or source file that existed before the run is never removed, and an
existing key is no longer overwritten.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
fix(update): remove the saved-packages directory when Docker is skipped or the update aborts
The timestamped docker-rollback directory is created before the new packages
are downloaded. When Docker is skipped, or the update aborts before any change,
that directory is removed, but only if this run created it: it is made with a
plain mkdir, so an existing directory is never adopted or removed. The apt
package lists are left as they are, since they are a cache.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
fix(update): replace the Traefik swap that renamed the running container
Renaming the running Traefik fails on swarm hosts where it is attached to
several networks ("cannot create entry in table endpoint_table ... already
exists"). The swap no longer renames anything, on the remote server path or
in the panel host installer.
The old container is stopped and removed with docker rm (no force). The new
one is created with --restart no, connected to dokploy-network and to the
captured networks (bridge excluded), and started. After the settle time it
must report running with no restarts, and only then is its restart policy
raised to always.
A failure after the old container was stopped removes whatever carries the
name, recreates the old image with the same flags and networks, starts it and
checks it, then exits 1 naming the step that failed. A leftover
dokploy-traefik-previous from earlier runs is removed first.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
fix(servers): fail the Nixpacks and Railpack updates when their install script does not download
The steps ran bash -c "$(curl ...)". When the download failed, the command
substitution came out empty, bash ran nothing, and the update reported
success. Each install script is now downloaded to a file with curl -fsSL,
checked for content and syntax, and only then run; any failure exits non-zero.
These steps do not touch running services, so they need no pre-download.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
docs: describe the Traefik replacement without renaming
The install guide still described the swap as renaming the running Traefik
container and gave a manual rollback that used docker rename. It now describes
stop, remove, create with --restart no, connect, start, settle check, and then
--restart always, and gives a manual rollback that recreates the container from
the previous image.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
fix(servers): fail the server setup when the Nixpacks or Railpack install script does not download
The setup ran bash -c "$(curl -fsSL ...)". When the download failed, the
command substitution came out empty, bash ran nothing, and the setup went on
to report Nixpacks or Railpack as installed. Each install script is now
downloaded to a file with curl -fsSL, checked for content and syntax, and only
then run with the pinned version in its environment; any failure stops the
setup with a non-zero exit.
Tests check the generated setup script, and run the Nixpacks, Railpack, RClone
and Buildpacks steps with a failing download and a working one.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
fix(update): fail the installer when the Nixpacks or Railpack install script does not download
install_toolchain ran bash -c "$(curl -fsSL ...)" for Nixpacks and Railpack, so
a failed download ran nothing and the install went on. Both scripts now go
through one helper that downloads to a file, checks it, runs it with the
pinned version in its environment, and exits non-zero on any failure.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
test(servers): check that failed Buildpacks and RClone downloads fail the update
The Buildpacks step pipes curl into tar and the RClone step downloads with
curl -fsSL before unzip. Both already exit non-zero when the download fails,
because tar rejects empty and HTML input and set -e stops the script. These
run tests keep it that way.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
fix(update): reset Traefik's restart policy before the Docker rollback starts it
A Docker rollback starts dokploy-traefik again. If the Traefik swap had left its
restart policy at "no", a daemon restart during the rollback would leave Traefik
down. The rollback now sets --restart always before it starts Traefik, on the
remote-server path and in the panel host installer.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
fix(update): verify Docker within its own window, 30 s on remote servers and 120 s on the panel host
A failed Docker upgrade on a remote server waited 60 seconds before it rolled
back. The wait is now 30 one-second checks, and the rollback starts as soon as
they expire. On the panel host, the Docker checks use DOKPLOY_DOCKER_VERIFY_TIMEOUT
(120 seconds by default) instead of DOKPLOY_HEALTH_TIMEOUT, so the health timeout
of the panel update does not change how long Docker is given. The new variable
passes through the macOS and WSL launchers, and the install guide describes both
windows.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---
fix(update): poll the new Traefik during the settle time and stop at the first failed check
The swap waited the full settle time before checking the new Traefik container,
so a container that exited early still kept the ports dark until the wait ended.
Both paths now check it once a second over the settle time, with State.Running
and RestartCount, and roll back as soon as it is not running. The final check
after the settle time stays, and the restart policy is raised only after it.
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
---1 parent d1e3bcd commit 003cc1b
36 files changed
Lines changed: 16637 additions & 231 deletions
File tree
- .github/workflows
- apps/dokploy
- __test__
- server
- settings
- components/dashboard
- application/build
- settings/servers
- drizzle
- meta
- server/api
- routers
- utils
- docs
- packages/server/src
- db/schema
- services
- setup
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
207 | 207 | | |
208 | 208 | | |
209 | 209 | | |
210 | | - | |
| 210 | + | |
211 | 211 | | |
212 | 212 | | |
213 | 213 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
33 | | - | |
| 33 | + | |
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
| |||
54 | 54 | | |
55 | 55 | | |
56 | 56 | | |
57 | | - | |
| 57 | + | |
58 | 58 | | |
59 | 59 | | |
60 | 60 | | |
| |||
66 | 66 | | |
67 | 67 | | |
68 | 68 | | |
69 | | - | |
| 69 | + | |
70 | 70 | | |
71 | 71 | | |
72 | 72 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
32 | | - | |
| 32 | + | |
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| |||
79 | 79 | | |
80 | 80 | | |
81 | 81 | | |
82 | | - | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
83 | 93 | | |
84 | 94 | | |
85 | 95 | | |
| |||
92 | 102 | | |
93 | 103 | | |
94 | 104 | | |
95 | | - | |
| 105 | + | |
96 | 106 | | |
97 | 107 | | |
98 | 108 | | |
99 | 109 | | |
100 | | - | |
| 110 | + | |
101 | 111 | | |
102 | 112 | | |
103 | 113 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | | - | |
| 2 | + | |
3 | 3 | | |
4 | 4 | | |
5 | 5 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
0 commit comments