mirror of
https://github.com/nextcloud/all-in-one.git
synced 2026-05-21 19:00:33 +00:00
Compare commits
213 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
324d75bbee | ||
|
|
5d787e9167 | ||
|
|
a2047dc04b | ||
|
|
5dfc30afa5 | ||
|
|
301f30dd2c | ||
|
|
dd751a9fe4 | ||
|
|
265c0d563b | ||
|
|
43aba16204 | ||
|
|
324a1156a0 | ||
|
|
d994512140 | ||
|
|
130ca73dd2 | ||
|
|
456f26b9d5 | ||
|
|
37997cc091 | ||
|
|
f3a7dfafa2 | ||
|
|
6be5bb6370 | ||
|
|
a6ffb5495e | ||
|
|
3e59616b5d | ||
|
|
9930a368f0 | ||
|
|
ed319492f1 | ||
|
|
a0cef69483 | ||
|
|
d37a0f509d | ||
|
|
3c34963504 | ||
|
|
460469feb4 | ||
|
|
c642f03d43 | ||
|
|
1c172b4dd7 | ||
|
|
3a79002322 | ||
|
|
8aeee368d4 | ||
|
|
541aa9c6c0 | ||
|
|
627e9d325f | ||
|
|
1c85e3e825 | ||
|
|
c4a8fef8e9 | ||
|
|
76cbefafb5 | ||
|
|
68447c9211 | ||
|
|
810d0590eb | ||
|
|
3c81a90920 | ||
|
|
d2cadf6b9f | ||
|
|
18b3c76a67 | ||
|
|
7312dac0f8 | ||
|
|
dfafec2314 | ||
|
|
947be33fcf | ||
|
|
340f7450ca | ||
|
|
09bbe2fd87 | ||
|
|
e8d66a06d1 | ||
|
|
c66b31901a | ||
|
|
94f0a799fc | ||
|
|
c00a1efdac | ||
|
|
73227b4f9d | ||
|
|
1a65c49331 | ||
|
|
9ce1434f34 | ||
|
|
8a62b4a2ea | ||
|
|
9febf2bade | ||
|
|
098e1347a1 | ||
|
|
74579ba7ea | ||
|
|
5dc4fbdb21 | ||
|
|
931b92b8b9 | ||
|
|
888d16d790 | ||
|
|
21086df922 | ||
|
|
5185a9f4ae | ||
|
|
7eba523e86 | ||
|
|
bcf36406a8 | ||
|
|
abf9684d0d | ||
|
|
cbb5e8f359 | ||
|
|
64c3f61b48 | ||
|
|
936b4ebb0f | ||
|
|
7db547732d | ||
|
|
912fa0697d | ||
|
|
68fd14bc86 | ||
|
|
0d4152a7f9 | ||
|
|
78fc3c1343 | ||
|
|
b4e18256a6 | ||
|
|
f9b13a7786 | ||
|
|
37b33c7f4b | ||
|
|
1f819e403b | ||
|
|
e3826f85b9 | ||
|
|
3e5ab69512 | ||
|
|
7d6695de7e | ||
|
|
06dd83f07a | ||
|
|
cd055c0838 | ||
|
|
1e3bb9ccfe | ||
|
|
7144eb84ff | ||
|
|
def87f38e8 | ||
|
|
0a63b49504 | ||
|
|
e672a5029b | ||
|
|
e22c6d6cef | ||
|
|
2290c0aede | ||
|
|
e448bc1f64 | ||
|
|
fb898b0ab3 | ||
|
|
9cb2801e0f | ||
|
|
d7a528075a | ||
|
|
9c80cc1bb3 | ||
|
|
8d93da24d6 | ||
|
|
faaa59e29a | ||
|
|
3b1014d34d | ||
|
|
3e9b191f94 | ||
|
|
b95bb65af8 | ||
|
|
b72a1c0e59 | ||
|
|
8c7e0bc63a | ||
|
|
f5e8e5630d | ||
|
|
89ff6ea408 | ||
|
|
814f4942f5 | ||
|
|
0941b6dee0 | ||
|
|
ae36403762 | ||
|
|
30eaaaa5f9 | ||
|
|
92a96ca245 | ||
|
|
59a49c62b4 | ||
|
|
0b9108f566 | ||
|
|
7c04f844a1 | ||
|
|
c2056ec215 | ||
|
|
8032f5b966 | ||
|
|
c79d46b868 | ||
|
|
1e7161d524 | ||
|
|
3eede90e86 | ||
|
|
33c822fd1e | ||
|
|
32935d1c33 | ||
|
|
72ca611d2f | ||
|
|
6c6c56fa1b | ||
|
|
6aa0b7097a | ||
|
|
89caf9d725 | ||
|
|
90ba2f7e92 | ||
|
|
55f4a8ec7f | ||
|
|
5f130528ce | ||
|
|
9cffb5a6ee | ||
|
|
164f7026b0 | ||
|
|
a89358ead8 | ||
|
|
31bba7ab80 | ||
|
|
7d49155fc1 | ||
|
|
9dfe5ab770 | ||
|
|
87e018b55f | ||
|
|
fa916c95a7 | ||
|
|
dbef6f1d4a | ||
|
|
fe83acd3cc | ||
|
|
3a0ad6a66f | ||
|
|
1976223efb | ||
|
|
9ff9a7901c | ||
|
|
9c6425308f | ||
|
|
52c926e052 | ||
|
|
1854417c28 | ||
|
|
9cc4393e99 | ||
|
|
cfa207916e | ||
|
|
7586ecefad | ||
|
|
12e30fe316 | ||
|
|
4c50b606bc | ||
|
|
38bd04ded9 | ||
|
|
95ba20d7f9 | ||
|
|
ff042fc742 | ||
|
|
8fedcb4810 | ||
|
|
9a85799220 | ||
|
|
7fc95050d4 | ||
|
|
666064389b | ||
|
|
7c333037fc | ||
|
|
955692a874 | ||
|
|
f2b26d262f | ||
|
|
c46b81da93 | ||
|
|
be0bad4740 | ||
|
|
f40f4a54b6 | ||
|
|
1b86445b6a | ||
|
|
7bcd0c16c0 | ||
|
|
68ac85e60b | ||
|
|
8ce9bd2cc2 | ||
|
|
ea098a4d1a | ||
|
|
b5e45e270b | ||
|
|
1fc8f2dd0f | ||
|
|
8317b30658 | ||
|
|
1a62857df7 | ||
|
|
172ae49cdf | ||
|
|
d385d43af0 | ||
|
|
064ea0f931 | ||
|
|
7f645b1c1a | ||
|
|
38726f039a | ||
|
|
79fdbee6b4 | ||
|
|
4c304d8775 | ||
|
|
8a21f2a8f5 | ||
|
|
1be6c725c2 | ||
|
|
c6d40e91e0 | ||
|
|
4fc4ca1e3e | ||
|
|
31f86c1570 | ||
|
|
a8d380ec7a | ||
|
|
6dd88239d1 | ||
|
|
ead69baae3 | ||
|
|
6be1154914 | ||
|
|
f9772a856c | ||
|
|
83ae27ef76 | ||
|
|
e60949aec9 | ||
|
|
817df30465 | ||
|
|
07ad9a7eb8 | ||
|
|
9c96806084 | ||
|
|
0e1edde3a7 | ||
|
|
8661bdf020 | ||
|
|
c3579476c6 | ||
|
|
ae349b8afb | ||
|
|
20bf2cfa7e | ||
|
|
0cbffeb90a | ||
|
|
8983a97ef6 | ||
|
|
0670c7cedf | ||
|
|
cd00e5af83 | ||
|
|
2c7146b15f | ||
|
|
975f4220d4 | ||
|
|
6beaa3b391 | ||
|
|
6b3cba411e | ||
|
|
ab747de69d | ||
|
|
480b57178b | ||
|
|
c9650aa0ef | ||
|
|
0e660f4c04 | ||
|
|
6722dee1d6 | ||
|
|
79af222c2d | ||
|
|
79473fac76 | ||
|
|
9b11c817b2 | ||
|
|
fb05818764 | ||
|
|
15a289e820 | ||
|
|
d5868a9626 | ||
|
|
22d3b0ba30 | ||
|
|
2b98a936b9 | ||
|
|
3263184e8f |
32
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
Normal file
32
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
name: 🐛 Bug report
|
||||
about: Help us improving by reporting a bug
|
||||
labels: bug, 0. Needs triage
|
||||
---
|
||||
|
||||
<!--- Please keep this note for other contributors -->
|
||||
### How to use GitHub
|
||||
|
||||
* Please use the 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to show that you are affected by the same issue.
|
||||
* Please don't comment if you have no relevant information to add. It's just extra noise for everyone subscribed to this issue.
|
||||
* Subscribe to receive notifications on status change and new comments.
|
||||
|
||||
<!--- Please fill out the whole template below -->
|
||||
### Steps to reproduce
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
|
||||
### Expected behaviour <!--- Tell us what should happen -->
|
||||
|
||||
### Actual behaviour <!--- Tell us what happens instead -->
|
||||
|
||||
|
||||
### Host OS <!--- (the host OS on which you are trying to install AIO on) -->
|
||||
|
||||
|
||||
#### Nextcloud AIO version <!--- (see Nextcloud AIO interface) -->
|
||||
|
||||
#### Current channel <!--- (see the channel name in the AIO interface) -->
|
||||
|
||||
#### Other valuable info <!--- (like logs, screenshots & Co.) -->
|
||||
24
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
Normal file
24
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
name: 📖 Existing feature/documentation enhancement
|
||||
about: Suggest an enhancement of an existing feature/documentation - for other types, please use the feature request option below
|
||||
labels: enhancement, 0. Needs triage
|
||||
---
|
||||
|
||||
<!--- Please keep this note for other contributors -->
|
||||
### How to use GitHub
|
||||
* Please use the 👍 [reaction](https://blog.github.com/2016-03-10-add-reactions-to-pull-requests-issues-and-comments/) to show that you are interested into the same feature.
|
||||
* Please don't comment if you have no relevant information to add. It's just extra noise for everyone subscribed to this issue.
|
||||
* Subscribe to receive notifications on status change and new comments.
|
||||
|
||||
<!--- Please fill out the whole template below -->
|
||||
### Is your feature request related to a problem? Please describe.
|
||||
<!--- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
|
||||
|
||||
### Describe the solution you'd like
|
||||
<!--- A clear and concise description of what you want to happen. -->
|
||||
|
||||
### Describe alternatives you've considered
|
||||
<!--- A clear and concise description of any alternative solutions or features you've considered. -->
|
||||
|
||||
### Additional context
|
||||
<!--- Add any other context or screenshots about the feature request below. -->
|
||||
14
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
14
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 💡 Suggest a new feature or discuss one
|
||||
url: https://github.com/nextcloud/all-in-one/discussions/categories/ideas
|
||||
about: For new feature requests and discussion of existing ones
|
||||
- name: ❓ Questions on AIO
|
||||
url: https://github.com/nextcloud/all-in-one/discussions/categories/questions
|
||||
about: For questions regarding AIO
|
||||
- name: ⛑️ Community Support and Help
|
||||
url: https://help.nextcloud.com/tag/aio
|
||||
about: For other types of questions
|
||||
- name: 💼 Nextcloud Enterprise
|
||||
url: https://portal.nextcloud.com/
|
||||
about: If you are a Nextcloud Enterprise customer, or need Professional support, so it can be resolved directly by our dedicated engineers more quickly
|
||||
25
.github/dependabot.yml
vendored
25
.github/dependabot.yml
vendored
@@ -1,9 +1,14 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 10
|
||||
- package-ecosystem: composer
|
||||
directory: "/php/"
|
||||
schedule:
|
||||
interval: daily
|
||||
interval: "daily"
|
||||
time: "12:00"
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
@@ -108,3 +113,21 @@ updates:
|
||||
labels:
|
||||
- 3. to review
|
||||
- dependencies
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/Containers/clamav"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
time: "12:00"
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- 3. to review
|
||||
- dependencies
|
||||
- package-ecosystem: "docker"
|
||||
directory: "/Containers/onlyoffice"
|
||||
schedule:
|
||||
interval: "daily"
|
||||
time: "12:00"
|
||||
open-pull-requests-limit: 10
|
||||
labels:
|
||||
- 3. to review
|
||||
- dependencies
|
||||
|
||||
16
.github/workflows/dependency-updates.yml
vendored
16
.github/workflows/dependency-updates.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
name: Run dependency update script
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- uses: nanasess/setup-php@master
|
||||
with:
|
||||
php-version: '8.0'
|
||||
@@ -31,8 +31,20 @@ jobs:
|
||||
done
|
||||
echo "outdated dependencies:
|
||||
$(composer outdated)"
|
||||
- name: Update apcu
|
||||
run: |
|
||||
# APCU
|
||||
apcu_version="$(
|
||||
git ls-remote --tags https://github.com/krakjoe/apcu.git \
|
||||
| cut -d/ -f3 \
|
||||
| grep -vE -- '-rc|-b' \
|
||||
| sed -E 's/^v//' \
|
||||
| sort -V \
|
||||
| tail -1
|
||||
)"
|
||||
sed -i "s|pecl install APCu.*\;|pecl install APCu-$apcu_version\;|" ./Containers/mastercontainer/Dockerfile
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v3
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
commit-message: dependency updates
|
||||
signoff: true
|
||||
|
||||
20
.github/workflows/lock-threads.yml
vendored
Normal file
20
.github/workflows/lock-threads.yml
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
name: 'Lock Threads'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
concurrency:
|
||||
group: lock
|
||||
|
||||
jobs:
|
||||
action:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: dessant/lock-threads@v3
|
||||
with:
|
||||
issue-inactive-days: '14'
|
||||
process-only: 'issues'
|
||||
4
.github/workflows/nextcloud-update.yml
vendored
4
.github/workflows/nextcloud-update.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
name: Run nextcloud-update script
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run nextcloud-update script
|
||||
run: |
|
||||
# Inspired by https://github.com/nextcloud/docker/blob/master/update.sh
|
||||
@@ -64,7 +64,7 @@ jobs:
|
||||
sed -i "s|^ENV NEXTCLOUD_VERSION.*|ENV NEXTCLOUD_VERSION $NCVERSION|" ./Containers/nextcloud/Dockerfile
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v3
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
commit-message: nextcloud-update automated change
|
||||
signoff: true
|
||||
|
||||
2
.github/workflows/psalm-analysis.yml
vendored
2
.github/workflows/psalm-analysis.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: Psalm
|
||||
uses: docker://ghcr.io/nextcloud/all-in-one-psalm
|
||||
with:
|
||||
|
||||
2
.github/workflows/psalm-security.yml
vendored
2
.github/workflows/psalm-security.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: Psalm
|
||||
uses: docker://ghcr.io/nextcloud/all-in-one-psalm
|
||||
with:
|
||||
|
||||
4
.github/workflows/psalm-update-baseline.yml
vendored
4
.github/workflows/psalm-update-baseline.yml
vendored
@@ -10,7 +10,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up php8.0
|
||||
uses: shivammathur/setup-php@v2
|
||||
@@ -31,7 +31,7 @@ jobs:
|
||||
continue-on-error: true
|
||||
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@v3
|
||||
uses: peter-evans/create-pull-request@v4
|
||||
with:
|
||||
token: ${{ secrets.COMMAND_BOT_PAT }}
|
||||
commit-message: Update psalm baseline
|
||||
|
||||
2
.github/workflows/shellcheck.yml
vendored
2
.github/workflows/shellcheck.yml
vendored
@@ -9,7 +9,7 @@ jobs:
|
||||
name: Github Actions
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v3
|
||||
- name: Run Shellcheck
|
||||
uses: ludeeus/action-shellcheck@master
|
||||
with:
|
||||
|
||||
4
.github/workflows/spellcheck.yml
vendored
4
.github/workflows/spellcheck.yml
vendored
@@ -10,7 +10,9 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: spelling or typos
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
- name: fix permission for reviewdog
|
||||
run: sudo chown -R root:root $GITHUB_WORKSPACE
|
||||
- name: misspell
|
||||
uses: reviewdog/action-misspell@v1
|
||||
with:
|
||||
|
||||
@@ -31,6 +31,14 @@
|
||||
reverse_proxy {$COLLABORA_HOST}:9980
|
||||
}
|
||||
|
||||
# Onlyoffice
|
||||
route /onlyoffice/* {
|
||||
uri strip_prefix /onlyoffice
|
||||
reverse_proxy {$ONLYOFFICE_HOST}:80 {
|
||||
header_up X-Forwarded-Host {http.request.host}/onlyoffice
|
||||
}
|
||||
}
|
||||
|
||||
# Nextcloud
|
||||
route {
|
||||
rewrite /.well-known/carddav /remote.php/dav
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
FROM debian:bullseye-20220228-slim
|
||||
# Caddy is a requirement
|
||||
FROM caddy:2.4.6-alpine as caddy
|
||||
|
||||
FROM debian:bullseye-20220418-slim
|
||||
|
||||
EXPOSE 80
|
||||
|
||||
@@ -21,10 +24,8 @@ RUN set -ex; \
|
||||
; \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN set -ex; \
|
||||
wget "https://caddyserver.com/api/download?os=linux&arch=$(dpkg-architecture --query DEB_BUILD_ARCH)" -O "/usr/bin/caddy" \
|
||||
&& chmod +x /usr/bin/caddy \
|
||||
&& /usr/bin/caddy version
|
||||
COPY --from=caddy /usr/bin/caddy /usr/bin/
|
||||
RUN chmod +x /usr/bin/caddy
|
||||
|
||||
RUN a2enmod rewrite \
|
||||
headers \
|
||||
|
||||
@@ -17,12 +17,6 @@ while ! nc -z "$NEXTCLOUD_HOST" 9000; do
|
||||
sleep 5
|
||||
done
|
||||
|
||||
# Only start container if collabora is started
|
||||
while ! nc -z "$COLLABORA_HOST" 9980; do
|
||||
echo "Waiting for Collabora to start..."
|
||||
sleep 5
|
||||
done
|
||||
|
||||
if [ -z "$APACHE_PORT" ]; then
|
||||
export APACHE_PORT="443"
|
||||
fi
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM debian:bullseye-20220125-slim
|
||||
FROM debian:bullseye-20220418-slim
|
||||
|
||||
RUN set -ex; \
|
||||
\
|
||||
@@ -8,6 +8,7 @@ RUN set -ex; \
|
||||
rsync \
|
||||
fuse \
|
||||
python3-llfuse \
|
||||
jq \
|
||||
; \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ if ! mountpoint -q /mnt/borgbackup; then
|
||||
fi
|
||||
|
||||
# Check if target is empty
|
||||
if [ "$BORG_MODE" != backup ] && ! [ -f "$BORG_BACKUP_DIRECTORY/config" ]; then
|
||||
if [ "$BORG_MODE" != backup ] && [ "$BORG_MODE" != test ] && ! [ -f "$BORG_BACKUP_DIRECTORY/config" ]; then
|
||||
echo "The repository is empty. cannot perform check or restore."
|
||||
exit 1
|
||||
fi
|
||||
@@ -173,17 +173,58 @@ if [ "$BORG_MODE" = restore ]; then
|
||||
echo "Could not mount the backup!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Restore everything except the configuration file
|
||||
if ! rsync --stats --archive --human-readable -vv --delete \
|
||||
--exclude "nextcloud_aio_mastercontainer/session/"** \
|
||||
--exclude "nextcloud_aio_mastercontainer/certs/"** \
|
||||
--exclude "nextcloud_aio_mastercontainer/data/daily_backup_running" \
|
||||
--exclude "nextcloud_aio_mastercontainer/data/configuration.json" \
|
||||
/tmp/borg/nextcloud_aio_volumes/ /nextcloud_aio_volumes; then
|
||||
echo "Something failed while restoring the boot partition."
|
||||
echo "Something failed while restoring from backup."
|
||||
umount /tmp/borg
|
||||
exit 1
|
||||
fi
|
||||
umount /tmp/borg
|
||||
|
||||
# TODO: reset fetchtimes in configuration.json so that it doesn't get the latest directly...
|
||||
# Save current aio password
|
||||
AIO_PASSWORD="$(jq '.password' /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json)"
|
||||
|
||||
# Save current path
|
||||
BORG_LOCATION="$(jq '.borg_backup_host_location' /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json)"
|
||||
|
||||
# Save current nextcloud datadir
|
||||
if grep -q '"nextcloud_datadir":' /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json; then
|
||||
NEXTCLOUD_DATADIR="$(jq '.nextcloud_datadir' /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json)"
|
||||
else
|
||||
NEXTCLOUD_DATADIR='""'
|
||||
fi
|
||||
|
||||
# Restore the configuration file
|
||||
if ! rsync --archive --human-readable -vv \
|
||||
/tmp/borg/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json \
|
||||
/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json; then
|
||||
echo "Something failed while restoring the configuration.json."
|
||||
umount /tmp/borg
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Set backup-mode to restore since it was a restore
|
||||
CONTENTS="$(jq '."backup-mode" = "restore"' /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json)"
|
||||
echo -E "${CONTENTS}" > /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json
|
||||
|
||||
# Reset the backup path to the currently used one
|
||||
CONTENTS="$(jq ".borg_backup_host_location = $BORG_LOCATION" /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json)"
|
||||
echo -E "${CONTENTS}" > /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json
|
||||
|
||||
# Reset the AIO password to the currently used one
|
||||
CONTENTS="$(jq ".password = $AIO_PASSWORD" /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json)"
|
||||
echo -E "${CONTENTS}" > /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json
|
||||
|
||||
# Reset the datadir to the one that was used for the restore
|
||||
CONTENTS="$(jq ".nextcloud_datadir = $NEXTCLOUD_DATADIR" /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json)"
|
||||
echo -E "${CONTENTS}" > /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json
|
||||
|
||||
umount /tmp/borg
|
||||
|
||||
# Inform user
|
||||
get_expiration_time
|
||||
@@ -192,17 +233,12 @@ if [ "$BORG_MODE" = restore ]; then
|
||||
# Add file to Nextcloud container so that it skips any update the next time
|
||||
touch "/nextcloud_aio_volumes/nextcloud_aio_nextcloud_data/skip.update"
|
||||
chmod 777 "/nextcloud_aio_volumes/nextcloud_aio_nextcloud_data/skip.update"
|
||||
|
||||
# Set backup-mode to restore since it was a restore
|
||||
sed -i 's/"backup-mode":"[a-z]\+"/"backup-mode":"restore"/g' /nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json
|
||||
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Do the Backup check
|
||||
if [ "$BORG_MODE" = check ]; then
|
||||
get_start_time
|
||||
echo "Checking the backup integity..."
|
||||
echo "Checking the backup integrity..."
|
||||
|
||||
# Perform the check
|
||||
if ! borg check --verify-data --progress "$BORG_BACKUP_DIRECTORY"; then
|
||||
@@ -215,3 +251,23 @@ if [ "$BORG_MODE" = check ]; then
|
||||
echo "Check finished successfully on $END_DATE_READABLE ($DURATION_READABLE)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Do the backup test
|
||||
if [ "$BORG_MODE" = test ]; then
|
||||
if ! [ -d "$BORG_BACKUP_DIRECTORY" ]; then
|
||||
echo "No 'borg' directory in the given backup directory found!"
|
||||
echo "Please adjust the directory so that the borg archive is positioned in a folder named 'borg' inside the given directory!"
|
||||
exit 1
|
||||
elif ! [ -f "$BORG_BACKUP_DIRECTORY/config" ]; then
|
||||
echo "A 'borg' directory was found but could not find the borg archive."
|
||||
echo "It must be positioned directly in the 'borg' subfolder."
|
||||
exit 1
|
||||
elif ! borg list "$BORG_BACKUP_DIRECTORY"; then
|
||||
echo "The entered path seems to be valid but could not open the backup archive."
|
||||
echo "Most likely the entered password was wrong so please adjust it accordingly!"
|
||||
exit 1
|
||||
else
|
||||
echo "Everything looks fine so feel free to continue!"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -4,19 +4,23 @@
|
||||
export BORG_BACKUP_DIRECTORY="/mnt/borgbackup/borg"
|
||||
|
||||
# Validate BORG_PASSWORD
|
||||
if [ -z "$BORG_PASSWORD" ]; then
|
||||
echo "BORG_PASSWORD is not allowed to be empty."
|
||||
if [ -z "$BORG_PASSWORD" ] && [ -z "$BACKUP_RESTORE_PASSWORD" ]; then
|
||||
echo "Neither BORG_PASSWORD nor BACKUP_RESTORE_PASSWORD are set."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Export defaults
|
||||
export BORG_PASSPHRASE="$BORG_PASSWORD"
|
||||
# Export defaults
|
||||
if [ -n "$BACKUP_RESTORE_PASSWORD" ]; then
|
||||
export BORG_PASSPHRASE="$BACKUP_RESTORE_PASSWORD"
|
||||
else
|
||||
export BORG_PASSPHRASE="$BORG_PASSWORD"
|
||||
fi
|
||||
export BORG_UNKNOWN_UNENCRYPTED_REPO_ACCESS_IS_OK=yes
|
||||
export BORG_RELOCATED_REPO_ACCESS_IS_OK=yes
|
||||
|
||||
# Validate BORG_MODE
|
||||
if [ "$BORG_MODE" != backup ] && [ "$BORG_MODE" != restore ] && [ "$BORG_MODE" != check ]; then
|
||||
echo "No correct BORG_MODE mode applied. Valid are 'backup' and 'restore'."
|
||||
if [ "$BORG_MODE" != backup ] && [ "$BORG_MODE" != restore ] && [ "$BORG_MODE" != check ] && [ "$BORG_MODE" != test ]; then
|
||||
echo "No correct BORG_MODE mode applied. Valid are 'backup', 'check', 'restore' and 'test'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -31,10 +35,8 @@ fi
|
||||
rm -f "/nextcloud_aio_volumes/nextcloud_aio_database_dump/backup-is-running"
|
||||
|
||||
# Get a list of all available borg archives
|
||||
set -x
|
||||
borg list "$BORG_BACKUP_DIRECTORY" | grep "nextcloud-aio" | awk -F " " '{print $1","$3,$4}' > "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/backup_archives.list"
|
||||
chmod +r "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/backup_archives.list"
|
||||
set +x
|
||||
|
||||
if [ -n "$FAILED" ]; then
|
||||
if [ "$BORG_MODE" = backup ]; then
|
||||
|
||||
5
Containers/clamav/Dockerfile
Normal file
5
Containers/clamav/Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
||||
# Probably from this file: https://github.com/Cisco-Talos/clamav/blob/main/Dockerfile
|
||||
FROM clamav/clamav:0.104.2-3
|
||||
|
||||
COPY clamav.conf /tmp/
|
||||
RUN cat /tmp/clamav.conf >> /etc/clamav/clamd.conf
|
||||
4
Containers/clamav/clamav.conf
Normal file
4
Containers/clamav/clamav.conf
Normal file
@@ -0,0 +1,4 @@
|
||||
MaxDirectoryRecursion 30
|
||||
MaxFileSize 100M
|
||||
PCREMaxFileSize 100M
|
||||
StreamMaxLength 100M
|
||||
@@ -1,2 +1,2 @@
|
||||
# From a file located probably somewhere here: https://github.com/CollaboraOnline/online/tree/master/docker
|
||||
FROM collabora/code:21.11.2.4.1
|
||||
FROM collabora/code:21.11.3.6.1
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM alpine:3.15.0
|
||||
FROM alpine:3.15.4
|
||||
RUN apk add --update --no-cache lighttpd bash
|
||||
|
||||
RUN adduser -S www-data -G www-data
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
# Docker CLI is a requirement
|
||||
FROM docker:20.10.12-dind-alpine3.15 as dind
|
||||
FROM docker:20.10.14-dind-alpine3.15 as dind
|
||||
|
||||
# Caddy is a requirement
|
||||
FROM caddy:2.4.6-alpine as caddy
|
||||
|
||||
# From https://github.com/docker-library/php/blob/master/8.0/bullseye/apache/Dockerfile
|
||||
FROM php:8.0.16-apache-bullseye
|
||||
FROM php:8.0.18-apache-bullseye
|
||||
|
||||
EXPOSE 80
|
||||
EXPOSE 8080
|
||||
@@ -23,20 +26,19 @@ RUN apt-get update; \
|
||||
openssl \
|
||||
sudo \
|
||||
dpkg-dev \
|
||||
netcat \
|
||||
; \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN set -ex; \
|
||||
curl "https://caddyserver.com/api/download?os=linux&arch=$(dpkg-architecture --query DEB_BUILD_ARCH)" -o "/usr/bin/caddy" \
|
||||
&& chmod +x /usr/bin/caddy \
|
||||
&& /usr/bin/caddy version
|
||||
COPY --from=caddy /usr/bin/caddy /usr/bin/
|
||||
RUN chmod +x /usr/bin/caddy
|
||||
|
||||
COPY --from=dind /usr/local/bin/docker /usr/local/bin/
|
||||
RUN chmod +x /usr/local/bin/docker
|
||||
|
||||
RUN mkdir -p /usr/src/php/ext/apcu && \
|
||||
curl -fsSL https://pecl.php.net/get/apcu | tar xvz -C "/usr/src/php/ext/apcu" --strip 1 && \
|
||||
docker-php-ext-install apcu
|
||||
RUN set -ex; \
|
||||
pecl install APCu-5.1.21; \
|
||||
docker-php-ext-enable apcu
|
||||
|
||||
RUN set -e && \
|
||||
curl -sS https://getcomposer.org/installer | php && \
|
||||
@@ -83,10 +85,15 @@ RUN mkdir /var/log/supervisord; \
|
||||
|
||||
COPY Caddyfile /
|
||||
COPY start.sh /usr/bin/
|
||||
COPY backup-time-file-watcher.sh /
|
||||
COPY session-deduplicator.sh /
|
||||
COPY cron.sh /
|
||||
COPY supervisord.conf /
|
||||
RUN chmod +x /usr/bin/start.sh; \
|
||||
chmod +x /cron.sh
|
||||
chmod +x /cron.sh; \
|
||||
chmod +x /session-deduplicator.sh; \
|
||||
chmod +x /backup-time-file-watcher.sh; \
|
||||
chmod a+r /Caddyfile
|
||||
|
||||
USER root
|
||||
|
||||
|
||||
30
Containers/mastercontainer/backup-time-file-watcher.sh
Normal file
30
Containers/mastercontainer/backup-time-file-watcher.sh
Normal file
@@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
|
||||
restart_process() {
|
||||
echo "Restarting cron.sh because daily backup time was set, changed or unset."
|
||||
pkill cron.sh
|
||||
}
|
||||
|
||||
file_present() {
|
||||
if [ -f "/mnt/docker-aio-config/data/daily_backup_time" ]; then
|
||||
if [ "$FILE_PRESENT" = 0 ]; then
|
||||
restart_process
|
||||
else
|
||||
if [ -n "$BACKUP_TIME" ] && [ "$(cat "/mnt/docker-aio-config/data/daily_backup_time")" != "$BACKUP_TIME" ]; then
|
||||
restart_process
|
||||
fi
|
||||
fi
|
||||
FILE_PRESENT=1
|
||||
BACKUP_TIME="$(cat "/mnt/docker-aio-config/data/daily_backup_time")"
|
||||
else
|
||||
if [ "$FILE_PRESENT" = 1 ]; then
|
||||
restart_process
|
||||
fi
|
||||
FILE_PRESENT=0
|
||||
fi
|
||||
}
|
||||
|
||||
while true; do
|
||||
file_present
|
||||
sleep 2
|
||||
done
|
||||
@@ -1,12 +1,96 @@
|
||||
#!/bin/sh
|
||||
set -eux
|
||||
#!/bin/bash
|
||||
|
||||
while true; do
|
||||
if [ -f "/mnt/docker-aio-config/data/daily_backup_time" ]; then
|
||||
set -x
|
||||
BACKUP_TIME="$(cat "/mnt/docker-aio-config/data/daily_backup_time")"
|
||||
DAILY_BACKUP=1
|
||||
set +x
|
||||
else
|
||||
BACKUP_TIME="04:00"
|
||||
DAILY_BACKUP=0
|
||||
fi
|
||||
|
||||
if [ -f "/mnt/docker-aio-config/data/daily_backup_running" ]; then
|
||||
LOCK_FILE_PRESENT=1
|
||||
else
|
||||
LOCK_FILE_PRESENT=0
|
||||
fi
|
||||
|
||||
# Allow to continue directly if e.g. the mastercontainer was updated. Otherwise wait for the next execution
|
||||
if [ "$LOCK_FILE_PRESENT" = 0 ]; then
|
||||
while [ "$(date +%H:%M)" != "$BACKUP_TIME" ]; do
|
||||
sleep 1
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "$DAILY_BACKUP" = 1 ]; then
|
||||
echo "Daily backup has started"
|
||||
|
||||
# Delete all active sessions and create a lock file
|
||||
# But don't kick out the user if the mastercontainer was just updated since we block the interface either way with the lock file
|
||||
if [ "$LOCK_FILE_PRESENT" = 0 ]; then
|
||||
rm -f "/mnt/docker-aio-config/session/"*
|
||||
fi
|
||||
sudo -u www-data touch "/mnt/docker-aio-config/data/daily_backup_running"
|
||||
|
||||
# Check if apache is running/stopped, watchtower is stopped and backupcontainer is stopped
|
||||
APACHE_PORT="$(docker inspect nextcloud-aio-apache --format "{{.HostConfig.PortBindings}}" | grep -oP '[0-9]+' | head -1)"
|
||||
while docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-apache$" && ! nc -z nextcloud-aio-apache "$APACHE_PORT"; do
|
||||
echo "Waiting for apache to become available"
|
||||
sleep 30
|
||||
done
|
||||
while docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-watchtower$"; do
|
||||
echo "Waiting for watchtower to stop"
|
||||
sleep 30
|
||||
done
|
||||
while docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-borgbackup$"; do
|
||||
echo "Waiting for borgbackup to stop"
|
||||
sleep 30
|
||||
done
|
||||
|
||||
# Update the mastercontainer
|
||||
sudo -u www-data php /var/www/docker-aio/php/src/Cron/UpdateMastercontainer.php
|
||||
|
||||
# Wait for watchtower to stop
|
||||
if ! docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-watchtower$"; then
|
||||
echo "Something seems to be wrong: Watchtower should be started at this step."
|
||||
else
|
||||
while docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-watchtower$"; do
|
||||
echo "Waiting for watchtower to stop"
|
||||
sleep 30
|
||||
done
|
||||
fi
|
||||
|
||||
# Execute the backup itself and some related tasks
|
||||
sudo -u www-data php /var/www/docker-aio/php/src/Cron/DailyBackup.php
|
||||
|
||||
# Delete the lock file
|
||||
rm -f "/mnt/docker-aio-config/data/daily_backup_running"
|
||||
|
||||
# Wait for the nextcloud container to start and send if the backup was successful
|
||||
if ! docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-nextcloud$"; then
|
||||
echo "Something seems to be wrong: Nextcloud should be started at this step."
|
||||
else
|
||||
while docker ps --format "{{.Names}}" | grep -q "^nextcloud-aio-nextcloud$" && ! nc -z nextcloud-aio-nextcloud 9000; do
|
||||
echo "Waiting for the Nextcloud container to start"
|
||||
sleep 30
|
||||
done
|
||||
fi
|
||||
sudo -u www-data php /var/www/docker-aio/php/src/Cron/BackupNotification.php
|
||||
|
||||
echo "Daily backup has finished"
|
||||
fi
|
||||
|
||||
# Make sure to delete the lock file always
|
||||
rm -f "/mnt/docker-aio-config/data/daily_backup_running"
|
||||
|
||||
# Check for updates and send notification if yes
|
||||
sudo -u www-data php /var/www/docker-aio/php/src/Cron/cron.php
|
||||
# Remove dangling images
|
||||
sudo -u www-data docker image prune -f
|
||||
sudo -u www-data php /var/www/docker-aio/php/src/Cron/UpdateNotification.php
|
||||
|
||||
# Remove sessions older than 24h
|
||||
find "/mnt/docker-aio-config/session/" -mindepth 1 -mmin +1440 -delete
|
||||
sleep 1d
|
||||
|
||||
# Remove dangling images
|
||||
sudo -u www-data docker image prune -f
|
||||
done
|
||||
|
||||
23
Containers/mastercontainer/session-deduplicator.sh
Normal file
23
Containers/mastercontainer/session-deduplicator.sh
Normal file
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
|
||||
while true; do
|
||||
while [ "$(find "/mnt/docker-aio-config/session/" -mindepth 1 -exec grep "aio_authenticated|[a-z]:1" {} \; | wc -l)" -gt 1 ]; do
|
||||
unset SESSION_FILES
|
||||
SESSION_FILES="$(find "/mnt/docker-aio-config/session/" -mindepth 1)"
|
||||
unset SESSION_FILES_ARRAY
|
||||
mapfile -t SESSION_FILES_ARRAY <<< "$SESSION_FILES"
|
||||
for SESSION_FILE in "${SESSION_FILES_ARRAY[@]}"; do
|
||||
if ! grep -q "aio_authenticated|[a-z]:1" "$SESSION_FILE"; then
|
||||
rm "$SESSION_FILE"
|
||||
fi
|
||||
done
|
||||
echo "Deleting duplicate sessions"
|
||||
unset OLDEST_FILE
|
||||
set -x
|
||||
# shellcheck disable=SC2012
|
||||
OLDEST_FILE="$(ls -t "/mnt/docker-aio-config/session/" | tail -1)"
|
||||
rm "/mnt/docker-aio-config/session/$OLDEST_FILE"
|
||||
set +x
|
||||
done
|
||||
sleep 5
|
||||
done
|
||||
@@ -43,7 +43,7 @@ elif ! sudo -u www-data test -r /var/run/docker.sock; then
|
||||
fi
|
||||
|
||||
# Check if api version is supported
|
||||
if ! docker info &>/dev/null; then
|
||||
if ! sudo -u www-data docker info &>/dev/null; then
|
||||
echo "Cannot connect to the docker socket. Cannot proceed."
|
||||
exit 1
|
||||
fi
|
||||
@@ -51,7 +51,7 @@ API_VERSION_FILE="$(find ./ -name DockerActionManager.php | head -1)"
|
||||
API_VERSION="$(grep -oP 'const API_VERSION.*\;' "$API_VERSION_FILE" | grep -oP '[0-9]+.[0-9]+' | head -1)"
|
||||
# shellcheck disable=SC2001
|
||||
API_VERSION_NUMB="$(echo "$API_VERSION" | sed 's/\.//')"
|
||||
LOCAL_API_VERSION_NUMB="$(docker version | grep -i "api version" | grep -oP '[0-9]+.[0-9]+' | head -1 | sed 's/\.//')"
|
||||
LOCAL_API_VERSION_NUMB="$(sudo -u www-data docker version | grep -i "api version" | grep -oP '[0-9]+.[0-9]+' | head -1 | sed 's/\.//')"
|
||||
if [ -n "$LOCAL_API_VERSION_NUMB" ] && [ -n "$API_VERSION_NUMB" ]; then
|
||||
if ! [ "$LOCAL_API_VERSION_NUMB" -ge "$API_VERSION_NUMB" ]; then
|
||||
echo "Docker v$API_VERSION is not supported by your docker engine. Cannot proceed."
|
||||
@@ -63,10 +63,10 @@ else
|
||||
fi
|
||||
|
||||
# Check if startup command was executed correctly
|
||||
if ! docker ps | grep -q "nextcloud-aio-mastercontainer"; then
|
||||
if ! sudo -u www-data docker ps | grep -q "nextcloud-aio-mastercontainer"; then
|
||||
echo "It seems like you did not give the mastercontainer the correct name?"
|
||||
exit 1
|
||||
elif ! docker volume ls | grep -q "nextcloud_aio_mastercontainer"; then
|
||||
elif ! sudo -u www-data docker volume ls | grep -q "nextcloud_aio_mastercontainer"; then
|
||||
echo "It seems like you did not give the mastercontainer volume the correct name?"
|
||||
exit 1
|
||||
fi
|
||||
@@ -74,24 +74,26 @@ fi
|
||||
# Check for other options
|
||||
if [ -n "$NEXTCLOUD_DATADIR" ]; then
|
||||
if ! echo "$NEXTCLOUD_DATADIR" | grep -q "^/mnt/" \
|
||||
&& ! echo "$NEXTCLOUD_DATADIR" | grep -q "^/media/"
|
||||
&& ! echo "$NEXTCLOUD_DATADIR" | grep -q "^/media/" \
|
||||
&& ! echo "$NEXTCLOUD_DATADIR" | grep -q "^/host_mnt/"
|
||||
then
|
||||
echo "You've set NEXTCLOUD_DATADIR but not to an allowed value.
|
||||
The string must start with '/mnt/' or '/media/'. E.g. '/mnt/ncdata'"
|
||||
The string must start with '/mnt/', '/media/' or '/host_mnt/'. E.g. '/mnt/ncdata'"
|
||||
exit 1
|
||||
elif [ "$NEXTCLOUD_DATADIR" = "/mnt/" ] || [ "$NEXTCLOUD_DATADIR" = "/media/" ]; then
|
||||
elif [ "$NEXTCLOUD_DATADIR" = "/mnt/" ] || [ "$NEXTCLOUD_DATADIR" = "/media/" ] || [ "$NEXTCLOUD_DATADIR" = "/host_mnt/" ]; then
|
||||
echo "You've set NEXTCLOUD_DATADIR but not to an allowed value.
|
||||
The string must start with '/mnt/' or '/media/' and not be equal to these."
|
||||
The string must start with '/mnt/', '/media/' or '/host_mnt/' and not be equal to these."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
if [ -n "$NEXTCLOUD_MOUNT" ]; then
|
||||
if ! echo "$NEXTCLOUD_MOUNT" | grep -q "^/mnt/" \
|
||||
&& ! echo "$NEXTCLOUD_MOUNT" | grep -q "^/media/" \
|
||||
&& ! echo "$NEXTCLOUD_MOUNT" | grep -q "^/host_mnt/" \
|
||||
&& ! echo "$NEXTCLOUD_MOUNT" | grep -q "^/var/backups$"
|
||||
then
|
||||
echo "You've set NEXCLOUD_MOUNT but not to an allowed value.
|
||||
The string must be equal to/start with '/mnt/' or '/media/' or be equal to '/var/backups'."
|
||||
The string must be equal to/start with '/mnt/', '/media/' or '/host_mnt/' or be equal to '/var/backups'."
|
||||
exit 1
|
||||
elif [ "$NEXTCLOUD_MOUNT" = "/mnt/ncdata" ] || echo "$NEXTCLOUD_MOUNT" | grep -q "^/mnt/ncdata/"; then
|
||||
echo "/mnt/ncdata and /mnt/ncdata/ are not allowed for NEXTCLOUD_MOUNT."
|
||||
@@ -125,7 +127,7 @@ chmod 770 -R /mnt/docker-aio-config
|
||||
chmod 777 /mnt/docker-aio-config
|
||||
chown www-data:www-data -R /mnt/docker-aio-config/data/
|
||||
chown www-data:www-data -R /mnt/docker-aio-config/session/
|
||||
chown root:root -R /mnt/docker-aio-config/caddy/
|
||||
chown www-data:www-data -R /mnt/docker-aio-config/caddy/
|
||||
chown root:root -R /mnt/docker-aio-config/certs/
|
||||
|
||||
# Adjust certs
|
||||
|
||||
@@ -20,7 +20,7 @@ stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
command=/usr/bin/caddy run -config /Caddyfile
|
||||
command=sudo -u www-data /usr/bin/caddy run -config /Caddyfile
|
||||
|
||||
[program:cron]
|
||||
stdout_logfile=/dev/stdout
|
||||
@@ -28,3 +28,17 @@ stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
command=/cron.sh
|
||||
|
||||
[program:backup-time-file-watcher]
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
command=/backup-time-file-watcher.sh
|
||||
|
||||
[program:session-deduplicator]
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
command=/session-deduplicator.sh
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# From https://github.com/nextcloud/docker/blob/master/23/fpm-alpine/Dockerfile
|
||||
FROM php:8.0.16-fpm-alpine3.15
|
||||
FROM php:8.0.18-fpm-alpine3.15
|
||||
|
||||
# Custom: change id of www-data user as it needs to be the same like on old installations
|
||||
RUN set -ex; \
|
||||
@@ -105,7 +105,7 @@ RUN { \
|
||||
VOLUME /var/www/html
|
||||
|
||||
|
||||
ENV NEXTCLOUD_VERSION 23.0.2
|
||||
ENV NEXTCLOUD_VERSION 23.0.3
|
||||
|
||||
RUN set -ex; \
|
||||
apk add --no-cache --virtual .fetch-deps \
|
||||
@@ -233,7 +233,8 @@ RUN set -ex; \
|
||||
chmod +x /entrypoint.sh && \
|
||||
chmod +r /upgrade.exclude && \
|
||||
chmod +x /cron.sh && \
|
||||
chmod +x /notify.sh
|
||||
chmod +x /notify.sh && \
|
||||
chmod +x /activate-collabora.sh
|
||||
|
||||
RUN set -ex; \
|
||||
mkdir /mnt/ncdata; \
|
||||
|
||||
20
Containers/nextcloud/activate-collabora.sh
Normal file
20
Containers/nextcloud/activate-collabora.sh
Normal file
@@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
|
||||
COLLABORA_ACTIVATED=0
|
||||
|
||||
while true; do
|
||||
if [ "$COLLABORA_ENABLED" != yes ]; then
|
||||
# Basically sleep for forever if collabora is not enabled
|
||||
sleep 365d
|
||||
fi
|
||||
if [ "$COLLABORA_ACTIVATED" != 0 ]; then
|
||||
# Basically sleep for forever if collabora was activated
|
||||
sleep 365d
|
||||
fi
|
||||
while ! nc -z "$NC_DOMAIN" 443; do
|
||||
sleep 5
|
||||
done
|
||||
echo "Activating collabora config"
|
||||
php /var/www/html/occ richdocuments:activate-config
|
||||
COLLABORA_ACTIVATED=1
|
||||
done
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
while true; do
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/bin/bash
|
||||
|
||||
# version_greater A B returns whether A > B
|
||||
version_greater() {
|
||||
@@ -211,9 +211,12 @@ if ! [ -f "/mnt/ncdata/skip.update" ]; then
|
||||
echo "Upgrading nextcloud from $installed_version to $image_version..."
|
||||
if ! php /var/www/html/occ upgrade || ! php /var/www/html/occ -V; then
|
||||
echo "Upgrade failed. Please restore from backup."
|
||||
bash /notify.sh "Nextcloud update to $image_version failed!" "Please restore from backup!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bash /notify.sh "Nextcloud update to $image_version successful!" "Feel free to inspect the Nextcloud container logs for more info."
|
||||
|
||||
php /var/www/html/occ app:list | sed -n "/Enabled:/,/Disabled:/p" > /tmp/list_after
|
||||
echo "The following apps have been disabled:"
|
||||
diff /tmp/list_before /tmp/list_after | grep '<' | cut -d- -f2 | cut -d: -f1
|
||||
@@ -230,6 +233,14 @@ if ! [ -f "/mnt/ncdata/skip.update" ]; then
|
||||
php /var/www/html/occ maintenance:mimetype:update-db
|
||||
fi
|
||||
fi
|
||||
|
||||
# Performing update of all apps if daily backups are enabled, running and successful
|
||||
if [ "$DAILY_BACKUP_RUNNING" = 'yes' ]; then
|
||||
UPDATED_APPS="$(php /var/www/html/occ app:update --all)"
|
||||
if [ -n "$UPDATED_APPS" ]; then
|
||||
bash /notify.sh "Your apps just got updated!" "$UPDATED_APPS"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if appdata is present
|
||||
@@ -251,6 +262,13 @@ php /var/www/html/occ config:system:set overwrite.cli.url --value="https://$NC_D
|
||||
php /var/www/html/occ config:system:set htaccess.RewriteBase --value="/"
|
||||
php /var/www/html/occ maintenance:update:htaccess
|
||||
|
||||
# Disallow creating local external storages when nothing was mounted
|
||||
if [ -z "$NEXTCLOUD_MOUNT" ]; then
|
||||
php /var/www/html/occ config:system:set files_external_allow_create_new_local --type=bool --value=false
|
||||
else
|
||||
php /var/www/html/occ config:system:set files_external_allow_create_new_local --type=bool --value=true
|
||||
fi
|
||||
|
||||
# AIO app
|
||||
if [ "$(php /var/www/html/occ config:app:get nextcloud-aio enabled)" = "" ]; then
|
||||
php /var/www/html/occ app:enable nextcloud-aio
|
||||
@@ -270,32 +288,89 @@ php /var/www/html/occ config:system:set trusted_proxies 0 --value="127.0.0.1"
|
||||
php /var/www/html/occ config:app:set notify_push base_endpoint --value="https://$NC_DOMAIN/push"
|
||||
|
||||
# Collabora
|
||||
if ! [ -d "/var/www/html/custom_apps/richdocuments" ]; then
|
||||
php /var/www/html/occ app:install richdocuments
|
||||
elif [ "$(php /var/www/html/occ config:app:get richdocuments enabled)" = "no" ]; then
|
||||
php /var/www/html/occ app:enable richdocuments
|
||||
if [ "$COLLABORA_ENABLED" = 'yes' ]; then
|
||||
if ! [ -d "/var/www/html/custom_apps/richdocuments" ]; then
|
||||
php /var/www/html/occ app:install richdocuments
|
||||
elif [ "$(php /var/www/html/occ config:app:get richdocuments enabled)" = "no" ]; then
|
||||
php /var/www/html/occ app:enable richdocuments
|
||||
else
|
||||
php /var/www/html/occ app:update richdocuments
|
||||
fi
|
||||
php /var/www/html/occ config:app:set richdocuments wopi_url --value="https://$NC_DOMAIN/"
|
||||
# Fix https://github.com/nextcloud/all-in-one/issues/188:
|
||||
php /var/www/html/occ config:system:set allow_local_remote_servers --type=bool --value=true
|
||||
else
|
||||
php /var/www/html/occ app:update richdocuments
|
||||
if [ -d "/var/www/html/custom_apps/richdocuments" ]; then
|
||||
php /var/www/html/occ config:system:delete allow_local_remote_servers
|
||||
php /var/www/html/occ app:remove richdocuments
|
||||
fi
|
||||
fi
|
||||
|
||||
# OnlyOffice
|
||||
if [ "$ONLYOFFICE_ENABLED" = 'yes' ]; then
|
||||
while ! nc -z "$ONLYOFFICE_HOST" 80; do
|
||||
echo "waiting for OnlyOffice to become available..."
|
||||
sleep 5
|
||||
done
|
||||
if ! [ -d "/var/www/html/custom_apps/onlyoffice" ]; then
|
||||
php /var/www/html/occ app:install onlyoffice
|
||||
elif [ "$(php /var/www/html/occ config:app:get onlyoffice enabled)" = "no" ]; then
|
||||
php /var/www/html/occ app:enable onlyoffice
|
||||
else
|
||||
php /var/www/html/occ app:update onlyoffice
|
||||
fi
|
||||
php /var/www/html/occ config:app:set onlyoffice DocumentServerUrl --value="https://$NC_DOMAIN/onlyoffice"
|
||||
else
|
||||
if [ -d "/var/www/html/custom_apps/onlyoffice" ]; then
|
||||
php /var/www/html/occ app:remove onlyoffice
|
||||
fi
|
||||
fi
|
||||
php /var/www/html/occ config:app:set richdocuments wopi_url --value="https://$NC_DOMAIN/"
|
||||
# php /var/www/html/occ richdocuments:activate-config
|
||||
# Fix https://github.com/nextcloud/all-in-one/issues/188:
|
||||
php /var/www/html/occ config:system:set allow_local_remote_servers --type=bool --value=true
|
||||
|
||||
# Talk
|
||||
if ! [ -d "/var/www/html/custom_apps/spreed" ]; then
|
||||
php /var/www/html/occ app:install spreed
|
||||
elif [ "$(php /var/www/html/occ config:app:get spreed enabled)" = "no" ]; then
|
||||
php /var/www/html/occ app:enable spreed
|
||||
if [ "$TALK_ENABLED" = 'yes' ]; then
|
||||
if ! [ -d "/var/www/html/custom_apps/spreed" ]; then
|
||||
php /var/www/html/occ app:install spreed
|
||||
elif [ "$(php /var/www/html/occ config:app:get spreed enabled)" = "no" ]; then
|
||||
php /var/www/html/occ app:enable spreed
|
||||
else
|
||||
php /var/www/html/occ app:update spreed
|
||||
fi
|
||||
STUN_SERVERS="[\"$NC_DOMAIN:3478\"]"
|
||||
TURN_SERVERS="[{\"server\":\"$NC_DOMAIN:3478\",\"secret\":\"$TURN_SECRET\",\"protocols\":\"udp,tcp\"}]"
|
||||
SIGNALING_SERVERS="{\"servers\":[{\"server\":\"https://$NC_DOMAIN/standalone-signaling/\",\"verify\":true}],\"secret\":\"$SIGNALING_SECRET\"}"
|
||||
php /var/www/html/occ config:app:set spreed stun_servers --value="$STUN_SERVERS" --output json
|
||||
php /var/www/html/occ config:app:set spreed turn_servers --value="$TURN_SERVERS" --output json
|
||||
php /var/www/html/occ config:app:set spreed signaling_servers --value="$SIGNALING_SERVERS" --output json
|
||||
else
|
||||
php /var/www/html/occ app:update spreed
|
||||
if [ -d "/var/www/html/custom_apps/spreed" ]; then
|
||||
php /var/www/html/occ app:remove spreed
|
||||
fi
|
||||
fi
|
||||
|
||||
# Clamav
|
||||
if [ "$CLAMAV_ENABLED" = 'yes' ]; then
|
||||
while ! nc -z "$CLAMAV_HOST" 3310; do
|
||||
echo "waiting for clamav to become available..."
|
||||
sleep 5
|
||||
done
|
||||
if ! [ -d "/var/www/html/custom_apps/files_antivirus" ]; then
|
||||
php /var/www/html/occ app:install files_antivirus
|
||||
elif [ "$(php /var/www/html/occ config:app:get files_antivirus enabled)" = "no" ]; then
|
||||
php /var/www/html/occ app:enable files_antivirus
|
||||
else
|
||||
php /var/www/html/occ app:update files_antivirus
|
||||
fi
|
||||
php /var/www/html/occ config:app:set files_antivirus av_mode --value="daemon"
|
||||
php /var/www/html/occ config:app:set files_antivirus av_port --value="3310"
|
||||
php /var/www/html/occ config:app:set files_antivirus av_host --value="$CLAMAV_HOST"
|
||||
php /var/www/html/occ config:app:set files_antivirus av_stream_max_length --value="104857600"
|
||||
php /var/www/html/occ config:app:set files_antivirus av_max_file_size --value="-1"
|
||||
php /var/www/html/occ config:app:set files_antivirus av_infected_action --value="only_log"
|
||||
else
|
||||
if [ -d "/var/www/html/custom_apps/files_antivirus" ]; then
|
||||
php /var/www/html/occ app:remove files_antivirus
|
||||
fi
|
||||
fi
|
||||
STUN_SERVERS="[\"$NC_DOMAIN:3478\"]"
|
||||
TURN_SERVERS="[{\"server\":\"$NC_DOMAIN:3478\",\"secret\":\"$TURN_SECRET\",\"protocols\":\"udp,tcp\"}]"
|
||||
SIGNALING_SERVERS="{\"servers\":[{\"server\":\"https://$NC_DOMAIN/standalone-signaling/\",\"verify\":true}],\"secret\":\"$SIGNALING_SECRET\"}"
|
||||
php /var/www/html/occ config:app:set spreed stun_servers --value="$STUN_SERVERS" --output json
|
||||
php /var/www/html/occ config:app:set spreed turn_servers --value="$TURN_SERVERS" --output json
|
||||
php /var/www/html/occ config:app:set spreed signaling_servers --value="$SIGNALING_SERVERS" --output json
|
||||
|
||||
# Remove the update skip file always
|
||||
rm -f /mnt/ncdata/skip.update
|
||||
|
||||
@@ -28,3 +28,10 @@ stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
command=/var/www/html/custom_apps/notify_push/bin/%(ENV_CPU_ARCH)s/notify_push /var/www/html/config/config.php --port 7867 --redis-url redis://:%(ENV_REDIS_HOST_PASSWORD)s@%(ENV_REDIS_HOST)s
|
||||
|
||||
[program:activate-collabora]
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
command=/activate-collabora.sh
|
||||
|
||||
2
Containers/onlyoffice/Dockerfile
Normal file
2
Containers/onlyoffice/Dockerfile
Normal file
@@ -0,0 +1,2 @@
|
||||
# From https://github.com/ONLYOFFICE/Docker-DocumentServer/blob/master/Dockerfile
|
||||
FROM onlyoffice/documentserver:7.0.1.37
|
||||
@@ -1,4 +1,4 @@
|
||||
FROM ubuntu:focal-20220302
|
||||
FROM ubuntu:focal-20220404
|
||||
|
||||
EXPOSE 3478
|
||||
|
||||
@@ -49,12 +49,16 @@ RUN chmod +x /usr/bin/start.sh; \
|
||||
sed -i '/TURNSERVER_ENABLED/c\TURNSERVER_ENABLED=1' /etc/default/coturn; \
|
||||
mkdir -p /var/tmp;
|
||||
|
||||
RUN curl -sL -o "/usr/share/janus/lua/json.lua" "https://raw.githubusercontent.com/rxi/json.lua/master/json.lua"; \
|
||||
curl -sL -o "/usr/share/janus/lua/ansicolors.lua" "https://raw.githubusercontent.com/kikito/ansicolors.lua/master/ansicolors.lua"
|
||||
|
||||
RUN mkdir -p /etc/nats; \
|
||||
echo "listen: 127.0.0.1:4222" > /etc/nats/nats.conf; \
|
||||
chown talk:talk /etc; \
|
||||
chown talk:talk -R /etc/nats; \
|
||||
chown talk:talk -R /etc/janus; \
|
||||
chown talk:talk -R /etc/signaling; \
|
||||
chown talk:talk -R /usr/share/janus
|
||||
chown talk:talk -R /usr
|
||||
|
||||
# Give root a random password
|
||||
RUN echo "root:$(openssl rand -base64 12)" | chpasswd
|
||||
|
||||
@@ -19,6 +19,7 @@ fi
|
||||
cat << TURN_CONF > "/etc/turnserver.conf"
|
||||
listening-port=3478
|
||||
fingerprint
|
||||
lt-cred-mech
|
||||
use-auth-secret
|
||||
static-auth-secret=$TURN_SECRET
|
||||
realm=$NC_DOMAIN
|
||||
@@ -31,33 +32,51 @@ pidfile=/var/tmp/turnserver.pid
|
||||
TURN_CONF
|
||||
|
||||
# Janus
|
||||
sed -i "s|#turn_rest_api_key.*|turn_rest_api_key = $JANUS_API_KEY|" /etc/janus/janus.jcfg
|
||||
sed -i "s|#full_trickle|full_trickle|g" /etc/janus/janus.jcfg
|
||||
set -x
|
||||
sed -i "s|#turn_rest_api_key.*|turn_rest_api_key = \"$JANUS_API_KEY\"|" /etc/janus/janus.jcfg
|
||||
sed -i "s|#full_trickle.*|full_trickle = true|g" /etc/janus/janus.jcfg
|
||||
sed -i 's|#stun_server.*|stun_server = "127.0.0.1"|g' /etc/janus/janus.jcfg
|
||||
sed -i "s|#stun_port.*|stun_port = 3478|g" /etc/janus/janus.jcfg
|
||||
sed -i "s|#turn_port.*|turn_port = 3478|g" /etc/janus/janus.jcfg
|
||||
sed -i 's|#turn_server.*|turn_server = "127.0.0.1"|g'/etc/janus/janus.jcfg
|
||||
sed -i 's|#turn_type .*|turn_type = "udp"|g' /etc/janus/janus.jcfg
|
||||
sed -i 's|#ice_ignore_list .*|ice_ignore_list = "udp"|g' /etc/janus/janus.jcfg
|
||||
sed -i 's|#interface.*|interface = "lo"|g' /etc/janus/janus.transport.websockets.jcfg
|
||||
sed -i 's|#ws_interface.*|ws_interface = "lo"|g' /etc/janus/janus.transport.websockets.jcfg
|
||||
set +x
|
||||
|
||||
# Signling
|
||||
cat << SIGNALING_CONF > "/etc/signaling/server.conf"
|
||||
[http]
|
||||
listen = 0.0.0.0:8081
|
||||
|
||||
[app]
|
||||
debug = false
|
||||
|
||||
[sessions]
|
||||
hashkey = $(openssl rand -hex 16)
|
||||
blockkey = $(openssl rand -hex 16)
|
||||
|
||||
[clients]
|
||||
internalsecret = $(openssl rand -hex 16)
|
||||
|
||||
[backend]
|
||||
allowed = ${NC_DOMAIN}
|
||||
backends = backend-1
|
||||
allowall = false
|
||||
secret = ${SIGNALING_SECRET}
|
||||
timeout = 10
|
||||
connectionsperhost = 8
|
||||
|
||||
[backend-1]
|
||||
url = https://${NC_DOMAIN}
|
||||
secret = ${SIGNALING_SECRET}
|
||||
|
||||
[nats]
|
||||
url = nats://127.0.0.1:4222
|
||||
|
||||
[mcu]
|
||||
type = janus
|
||||
url = ws://127.0.0.1:8188
|
||||
|
||||
[turn]
|
||||
apikey = ${JANUS_API_KEY}
|
||||
secret = ${TURN_SECRET}
|
||||
|
||||
@@ -27,7 +27,7 @@ stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
command=janus
|
||||
command=/usr/bin/janus --config=/etc/janus/janus.jcfg --disable-colors --daemon --log-stdout
|
||||
|
||||
[program:signaling]
|
||||
stdout_logfile=/dev/stdout
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# From https://github.com/containrrr/watchtower/blob/main/dockerfiles/Dockerfile.self-contained
|
||||
FROM containrrr/watchtower:1.4.0 as watchtower
|
||||
|
||||
FROM alpine:3.15.0
|
||||
FROM alpine:3.15.4
|
||||
|
||||
RUN apk add --update --no-cache bash
|
||||
COPY --from=watchtower /watchtower /
|
||||
|
||||
@@ -18,7 +18,12 @@ It will now also select the developer channel for all other containers automatic
|
||||
|
||||
Go to https://github.com/nextcloud-releases/all-in-one/actions/workflows/repo-sync.yml and run the workflow that will first sync the repo and then build new container that automatically get published to `develop` and `develop-arm64`.
|
||||
|
||||
## How to promote builds from develop to latest
|
||||
## How to promote builds from develop to beta
|
||||
|
||||
1. Verify that no job is running here: https://github.com/nextcloud-releases/all-in-one/actions/workflows/build_images.yml
|
||||
2. Go to https://github.com/nextcloud-releases/all-in-one/actions/workflows/promote-to-latest.yml, click on `Run workflow` and enter your desired container image name that you want to publish from develop to latest. Available image names are listed here: https://github.com/nextcloud-releases/all-in-one/blob/main/.github/workflows/build_images.yml#L21-L30
|
||||
2. Go to https://github.com/nextcloud-releases/all-in-one/actions/workflows/promote-to-beta.yml, click on `Run workflow`.
|
||||
|
||||
## How to promote builds from beta to latest
|
||||
|
||||
1. Verify that no job is running here: https://github.com/nextcloud-releases/all-in-one/actions/workflows/promote-to-beta.yml
|
||||
2. Go to https://github.com/nextcloud-releases/all-in-one/actions/workflows/promote-to-latest.yml, click on `Run workflow`.
|
||||
|
||||
220
php/composer.lock
generated
220
php/composer.lock
generated
@@ -8,16 +8,16 @@
|
||||
"packages": [
|
||||
{
|
||||
"name": "guzzlehttp/guzzle",
|
||||
"version": "7.4.1",
|
||||
"version": "7.4.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/guzzle.git",
|
||||
"reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79"
|
||||
"reference": "ac1ec1cd9b5624694c3a40be801d94137afb12b4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/ee0a041b1760e6a53d2a39c8c34115adc2af2c79",
|
||||
"reference": "ee0a041b1760e6a53d2a39c8c34115adc2af2c79",
|
||||
"url": "https://api.github.com/repos/guzzle/guzzle/zipball/ac1ec1cd9b5624694c3a40be801d94137afb12b4",
|
||||
"reference": "ac1ec1cd9b5624694c3a40be801d94137afb12b4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -112,7 +112,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/guzzle/issues",
|
||||
"source": "https://github.com/guzzle/guzzle/tree/7.4.1"
|
||||
"source": "https://github.com/guzzle/guzzle/tree/7.4.2"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -128,7 +128,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-12-06T18:43:05+00:00"
|
||||
"time": "2022-03-20T14:16:28+00:00"
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/promises",
|
||||
@@ -216,16 +216,16 @@
|
||||
},
|
||||
{
|
||||
"name": "guzzlehttp/psr7",
|
||||
"version": "2.1.0",
|
||||
"version": "2.2.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/guzzle/psr7.git",
|
||||
"reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72"
|
||||
"reference": "c94a94f120803a18554c1805ef2e539f8285f9a2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/089edd38f5b8abba6cb01567c2a8aaa47cec4c72",
|
||||
"reference": "089edd38f5b8abba6cb01567c2a8aaa47cec4c72",
|
||||
"url": "https://api.github.com/repos/guzzle/psr7/zipball/c94a94f120803a18554c1805ef2e539f8285f9a2",
|
||||
"reference": "c94a94f120803a18554c1805ef2e539f8285f9a2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -249,7 +249,7 @@
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "2.1-dev"
|
||||
"dev-master": "2.2-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
@@ -311,7 +311,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/guzzle/psr7/issues",
|
||||
"source": "https://github.com/guzzle/psr7/tree/2.1.0"
|
||||
"source": "https://github.com/guzzle/psr7/tree/2.2.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -327,7 +327,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-10-06T17:43:30+00:00"
|
||||
"time": "2022-03-20T21:55:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "http-interop/http-factory-guzzle",
|
||||
@@ -387,6 +387,65 @@
|
||||
},
|
||||
"time": "2021-07-21T13:50:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/serializable-closure",
|
||||
"version": "v1.1.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/serializable-closure.git",
|
||||
"reference": "9e4b005daa20b0c161f3845040046dc9ddc1d74e"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/9e4b005daa20b0c161f3845040046dc9ddc1d74e",
|
||||
"reference": "9e4b005daa20b0c161f3845040046dc9ddc1d74e",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.3|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"pestphp/pest": "^1.18",
|
||||
"phpstan/phpstan": "^0.12.98",
|
||||
"symfony/var-dumper": "^5.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\SerializableClosure\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
},
|
||||
{
|
||||
"name": "Nuno Maduro",
|
||||
"email": "nuno@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "Laravel Serializable Closure provides an easy and secure way to serialize closures in PHP.",
|
||||
"keywords": [
|
||||
"closure",
|
||||
"laravel",
|
||||
"serializable"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/serializable-closure/issues",
|
||||
"source": "https://github.com/laravel/serializable-closure"
|
||||
},
|
||||
"time": "2022-02-11T19:23:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/fast-route",
|
||||
"version": "v1.3.0",
|
||||
@@ -437,71 +496,6 @@
|
||||
},
|
||||
"time": "2018-02-13T20:26:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "opis/closure",
|
||||
"version": "3.6.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/opis/closure.git",
|
||||
"reference": "3d81e4309d2a927abbe66df935f4bb60082805ad"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/opis/closure/zipball/3d81e4309d2a927abbe66df935f4bb60082805ad",
|
||||
"reference": "3d81e4309d2a927abbe66df935f4bb60082805ad",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^5.4 || ^7.0 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"jeremeamia/superclosure": "^2.0",
|
||||
"phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "3.6.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"functions.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Opis\\Closure\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Marius Sarca",
|
||||
"email": "marius.sarca@gmail.com"
|
||||
},
|
||||
{
|
||||
"name": "Sorin Sarca",
|
||||
"email": "sarca_sorin@hotmail.com"
|
||||
}
|
||||
],
|
||||
"description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.",
|
||||
"homepage": "https://opis.io/closure",
|
||||
"keywords": [
|
||||
"anonymous functions",
|
||||
"closure",
|
||||
"function",
|
||||
"serializable",
|
||||
"serialization",
|
||||
"serialize"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/opis/closure/issues",
|
||||
"source": "https://github.com/opis/closure/tree/3.6.3"
|
||||
},
|
||||
"time": "2022-01-27T09:35:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-di/invoker",
|
||||
"version": "2.3.3",
|
||||
@@ -559,21 +553,21 @@
|
||||
},
|
||||
{
|
||||
"name": "php-di/php-di",
|
||||
"version": "6.3.5",
|
||||
"version": "6.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/PHP-DI/PHP-DI.git",
|
||||
"reference": "b8126d066ce144765300ee0ab040c1ed6c9ef588"
|
||||
"reference": "ae0f1b3b03d8b29dff81747063cbfd6276246cc4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/b8126d066ce144765300ee0ab040c1ed6c9ef588",
|
||||
"reference": "b8126d066ce144765300ee0ab040c1ed6c9ef588",
|
||||
"url": "https://api.github.com/repos/PHP-DI/PHP-DI/zipball/ae0f1b3b03d8b29dff81747063cbfd6276246cc4",
|
||||
"reference": "ae0f1b3b03d8b29dff81747063cbfd6276246cc4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"opis/closure": "^3.5.5",
|
||||
"php": ">=7.2.0",
|
||||
"laravel/serializable-closure": "^1.0",
|
||||
"php": ">=7.4.0",
|
||||
"php-di/invoker": "^2.0",
|
||||
"php-di/phpdoc-reader": "^2.0.1",
|
||||
"psr/container": "^1.0"
|
||||
@@ -582,12 +576,12 @@
|
||||
"psr/container-implementation": "^1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/annotations": "~1.2",
|
||||
"doctrine/annotations": "~1.10",
|
||||
"friendsofphp/php-cs-fixer": "^2.4",
|
||||
"mnapoli/phpunit-easymock": "^1.2",
|
||||
"ocramius/proxy-manager": "^2.0.2",
|
||||
"ocramius/proxy-manager": "^2.11.2",
|
||||
"phpstan/phpstan": "^0.12",
|
||||
"phpunit/phpunit": "^8.5|^9.0"
|
||||
"phpunit/phpunit": "^9.5"
|
||||
},
|
||||
"suggest": {
|
||||
"doctrine/annotations": "Install it if you want to use annotations (version ~1.2)",
|
||||
@@ -619,7 +613,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/PHP-DI/PHP-DI/issues",
|
||||
"source": "https://github.com/PHP-DI/PHP-DI/tree/6.3.5"
|
||||
"source": "https://github.com/PHP-DI/PHP-DI/tree/6.4.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -631,7 +625,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-09-02T09:49:58+00:00"
|
||||
"time": "2022-04-09T16:46:38+00:00"
|
||||
},
|
||||
{
|
||||
"name": "php-di/phpdoc-reader",
|
||||
@@ -1192,22 +1186,22 @@
|
||||
},
|
||||
{
|
||||
"name": "slim/slim",
|
||||
"version": "4.9.0",
|
||||
"version": "4.10.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/slimphp/Slim.git",
|
||||
"reference": "44d3c9c0bfcc47e52e42b097b6062689d21b904b"
|
||||
"reference": "0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/slimphp/Slim/zipball/44d3c9c0bfcc47e52e42b097b6062689d21b904b",
|
||||
"reference": "44d3c9c0bfcc47e52e42b097b6062689d21b904b",
|
||||
"url": "https://api.github.com/repos/slimphp/Slim/zipball/0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0",
|
||||
"reference": "0dfc7d2fdf2553b361d864d51af3fe8a6ad168b0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"nikic/fast-route": "^1.3",
|
||||
"php": "^7.3 || ^8.0",
|
||||
"php": "^7.4 || ^8.0",
|
||||
"psr/container": "^1.0 || ^2.0",
|
||||
"psr/http-factory": "^1.0",
|
||||
"psr/http-message": "^1.0",
|
||||
@@ -1218,13 +1212,15 @@
|
||||
"require-dev": {
|
||||
"adriansuter/php-autoload-override": "^1.2",
|
||||
"ext-simplexml": "*",
|
||||
"guzzlehttp/psr7": "^2.0",
|
||||
"guzzlehttp/psr7": "^2.1",
|
||||
"httpsoft/http-message": "^1.0",
|
||||
"httpsoft/http-server-request": "^1.0",
|
||||
"laminas/laminas-diactoros": "^2.8",
|
||||
"nyholm/psr7": "^1.4",
|
||||
"nyholm/psr7": "^1.5",
|
||||
"nyholm/psr7-server": "^1.0",
|
||||
"phpspec/prophecy": "^1.14",
|
||||
"phpspec/prophecy": "^1.15",
|
||||
"phpspec/prophecy-phpunit": "^2.0",
|
||||
"phpstan/phpstan": "^0.12.99",
|
||||
"phpstan/phpstan": "^1.4",
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"slim/http": "^1.2",
|
||||
"slim/psr7": "^1.5",
|
||||
@@ -1301,7 +1297,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-10-05T03:00:00+00:00"
|
||||
"time": "2022-03-14T14:18:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "slim/twig-view",
|
||||
@@ -1370,16 +1366,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v3.0.0",
|
||||
"version": "v3.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||
"reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced"
|
||||
"reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/c726b64c1ccfe2896cb7df2e1331c357ad1c8ced",
|
||||
"reference": "c726b64c1ccfe2896cb7df2e1331c357ad1c8ced",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/26954b3d62a6c5fd0ea8a2a00c0353a14978d05c",
|
||||
"reference": "26954b3d62a6c5fd0ea8a2a00c0353a14978d05c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1417,7 +1413,7 @@
|
||||
"description": "A generic function and convention to trigger deprecation notices",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.0"
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.0.1"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1433,7 +1429,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-11-01T23:48:49+00:00"
|
||||
"time": "2022-01-02T09:55:41+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-ctype",
|
||||
@@ -1681,16 +1677,16 @@
|
||||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v3.3.8",
|
||||
"version": "v3.3.10",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/twigphp/Twig.git",
|
||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c"
|
||||
"reference": "8442df056c51b706793adf80a9fd363406dd3674"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/972d8604a92b7054828b539f2febb0211dd5945c",
|
||||
"reference": "972d8604a92b7054828b539f2febb0211dd5945c",
|
||||
"url": "https://api.github.com/repos/twigphp/Twig/zipball/8442df056c51b706793adf80a9fd363406dd3674",
|
||||
"reference": "8442df056c51b706793adf80a9fd363406dd3674",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -1741,7 +1737,7 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/twigphp/Twig/issues",
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.3.8"
|
||||
"source": "https://github.com/twigphp/Twig/tree/v3.3.10"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -1753,7 +1749,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-02-04T06:59:48+00:00"
|
||||
"time": "2022-04-06T06:47:41+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
@@ -1770,5 +1766,5 @@
|
||||
"ext-apcu": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.2.0"
|
||||
"plugin-api-version": "2.3.0"
|
||||
}
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
"production": [
|
||||
{
|
||||
"dependsOn": [
|
||||
"nextcloud-aio-nextcloud",
|
||||
"nextcloud-aio-onlyoffice",
|
||||
"nextcloud-aio-collabora",
|
||||
"nextcloud-aio-talk"
|
||||
"nextcloud-aio-clamav",
|
||||
"nextcloud-aio-talk",
|
||||
"nextcloud-aio-nextcloud"
|
||||
],
|
||||
"identifier": "nextcloud-aio-apache",
|
||||
"displayName": "Apache",
|
||||
@@ -21,7 +23,8 @@
|
||||
"NEXTCLOUD_HOST=nextcloud-aio-nextcloud",
|
||||
"COLLABORA_HOST=nextcloud-aio-collabora",
|
||||
"TALK_HOST=nextcloud-aio-talk",
|
||||
"APACHE_PORT=%APACHE_PORT%"
|
||||
"APACHE_PORT=%APACHE_PORT%",
|
||||
"ONLYOFFICE_HOST=nextcloud-aio-onlyoffice"
|
||||
],
|
||||
"volumes": [
|
||||
{
|
||||
@@ -122,7 +125,16 @@
|
||||
"OVERWRITEPROTOCOL=https",
|
||||
"TURN_SECRET=%TURN_SECRET%",
|
||||
"SIGNALING_SECRET=%SIGNALING_SECRET%",
|
||||
"AIO_URL=%AIO_URL%"
|
||||
"AIO_URL=%AIO_URL%",
|
||||
"NEXTCLOUD_MOUNT=%NEXTCLOUD_MOUNT%",
|
||||
"CLAMAV_ENABLED=%CLAMAV_ENABLED%",
|
||||
"CLAMAV_HOST=nextcloud-aio-clamav",
|
||||
"ONLYOFFICE_ENABLED=%ONLYOFFICE_ENABLED%",
|
||||
"COLLABORA_ENABLED=%COLLABORA_ENABLED%",
|
||||
"COLLABORA_HOST=nextcloud-aio-collabora",
|
||||
"TALK_ENABLED=%TALK_ENABLED%",
|
||||
"ONLYOFFICE_HOST=nextcloud-aio-onlyoffice",
|
||||
"DAILY_BACKUP_RUNNING=%DAILY_BACKUP_RUNNING%"
|
||||
],
|
||||
"maxShutdownTime": 10,
|
||||
"restartPolicy": "unless-stopped"
|
||||
@@ -156,7 +168,7 @@
|
||||
"9980"
|
||||
],
|
||||
"environmentVariables": [
|
||||
"domain=%NC_DOMAIN%",
|
||||
"aliasgroup1=https://%NC_DOMAIN%:443",
|
||||
"extra_params=--o:ssl.enable=false --o:ssl.termination=true --o:logging.level=warning"
|
||||
],
|
||||
"volumes": [],
|
||||
@@ -201,7 +213,8 @@
|
||||
"environmentVariables": [
|
||||
"BORG_PASSWORD=%BORGBACKUP_PASSWORD%",
|
||||
"BORG_MODE=%BORGBACKUP_MODE%",
|
||||
"SELECTED_RESTORE_TIME=%SELECTED_RESTORE_TIME%"
|
||||
"SELECTED_RESTORE_TIME=%SELECTED_RESTORE_TIME%",
|
||||
"BACKUP_RESTORE_PASSWORD=%BACKUP_RESTORE_PASSWORD%"
|
||||
],
|
||||
"volumes": [
|
||||
{
|
||||
@@ -291,6 +304,48 @@
|
||||
],
|
||||
"maxShutdownTime": 1,
|
||||
"restartPolicy": ""
|
||||
},
|
||||
{
|
||||
"dependsOn": [],
|
||||
"identifier": "nextcloud-aio-clamav",
|
||||
"displayName": "ClamAV",
|
||||
"containerName": "nextcloud/aio-clamav",
|
||||
"ports": [],
|
||||
"internalPorts": [
|
||||
"3310"
|
||||
],
|
||||
"environmentVariables": [],
|
||||
"volumes": [
|
||||
{
|
||||
"name": "nextcloud_aio_clamav",
|
||||
"location": "/var/lib/clamav",
|
||||
"writeable": true
|
||||
}
|
||||
],
|
||||
"secrets": [],
|
||||
"maxShutdownTime": 10,
|
||||
"restartPolicy": "unless-stopped"
|
||||
},
|
||||
{
|
||||
"dependsOn": [],
|
||||
"identifier": "nextcloud-aio-onlyoffice",
|
||||
"displayName": "OnlyOffice",
|
||||
"containerName": "nextcloud/aio-onlyoffice",
|
||||
"ports": [],
|
||||
"internalPorts": [
|
||||
"80"
|
||||
],
|
||||
"environmentVariables": [],
|
||||
"volumes": [
|
||||
{
|
||||
"name": "nextcloud_aio_onlyoffice",
|
||||
"location": "/var/lib/onlyoffice",
|
||||
"writeable": true
|
||||
}
|
||||
],
|
||||
"secrets": [],
|
||||
"maxShutdownTime": 10,
|
||||
"restartPolicy": "unless-stopped"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -18,21 +18,10 @@
|
||||
<MissingParamType occurrences="1">
|
||||
<code>$args</code>
|
||||
</MissingParamType>
|
||||
<PossiblyInvalidArrayAccess occurrences="2">
|
||||
<code>$request->getParsedBody()['borg_backup_host_location']</code>
|
||||
<code>$request->getParsedBody()['domain']</code>
|
||||
</PossiblyInvalidArrayAccess>
|
||||
<PossiblyNullArgument occurrences="2">
|
||||
<code>$request->getParsedBody()['borg_backup_host_location']</code>
|
||||
<code>$request->getParsedBody()['domain']</code>
|
||||
</PossiblyNullArgument>
|
||||
<PossiblyNullArrayAccess occurrences="2">
|
||||
<code>$request->getParsedBody()['borg_backup_host_location']</code>
|
||||
<code>$request->getParsedBody()['domain']</code>
|
||||
</PossiblyNullArrayAccess>
|
||||
</file>
|
||||
<file src="src/Controller/DockerController.php">
|
||||
<MissingParamType occurrences="7">
|
||||
<MissingParamType occurrences="8">
|
||||
<code>$args</code>
|
||||
<code>$args</code>
|
||||
<code>$args</code>
|
||||
<code>$args</code>
|
||||
@@ -44,19 +33,9 @@
|
||||
<PossiblyInvalidArrayAccess occurrences="1">
|
||||
<code>$request->getParsedBody()['selected_restore_time']</code>
|
||||
</PossiblyInvalidArrayAccess>
|
||||
<PossiblyNullArgument occurrences="1">
|
||||
<code>$container</code>
|
||||
</PossiblyNullArgument>
|
||||
<PossiblyNullArrayAccess occurrences="1">
|
||||
<code>$request->getParsedBody()['selected_restore_time']</code>
|
||||
</PossiblyNullArrayAccess>
|
||||
<PossiblyNullReference occurrences="5">
|
||||
<code>GetDependsOn</code>
|
||||
<code>GetDependsOn</code>
|
||||
<code>GetIdentifier</code>
|
||||
<code>GetIdentifier</code>
|
||||
<code>GetRunningState</code>
|
||||
</PossiblyNullReference>
|
||||
</file>
|
||||
<file src="src/Controller/LoginController.php">
|
||||
<MissingParamType occurrences="3">
|
||||
@@ -74,12 +53,6 @@
|
||||
<code>$request->getParsedBody()['password']</code>
|
||||
</PossiblyNullArrayAccess>
|
||||
</file>
|
||||
<file src="src/Cron/cron.php">
|
||||
<PossiblyNullArgument occurrences="2">
|
||||
<code>$nextcloudContainer</code>
|
||||
<code>$nextcloudContainer</code>
|
||||
</PossiblyNullArgument>
|
||||
</file>
|
||||
<file src="src/Docker/DockerActionManager.php">
|
||||
<InvalidReturnType occurrences="1">
|
||||
<code>IContainerState</code>
|
||||
@@ -90,12 +63,6 @@
|
||||
<PossiblyFalseOperand occurrences="1">
|
||||
<code>strpos($fullDigest, "@")</code>
|
||||
</PossiblyFalseOperand>
|
||||
<PossiblyNullArgument occurrences="1">
|
||||
<code>$apacheContainer</code>
|
||||
</PossiblyNullArgument>
|
||||
<PossiblyNullReference occurrences="1">
|
||||
<code>GetUpdateState</code>
|
||||
</PossiblyNullReference>
|
||||
<RedundantCondition occurrences="1">
|
||||
<code>$container->GetInternalPorts() !== null</code>
|
||||
</RedundantCondition>
|
||||
|
||||
17
php/public/automatic_reload.js
Normal file
17
php/public/automatic_reload.js
Normal file
@@ -0,0 +1,17 @@
|
||||
if (document.hasFocus()) {
|
||||
// hide reload button if the site reloads automatically
|
||||
var list = document.getElementsByClassName("reload button");
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
// list[i] is a node with the desired class name
|
||||
list[i].style.display = 'none';
|
||||
}
|
||||
|
||||
// set timeout for reload
|
||||
setTimeout(function(){
|
||||
window.location.reload(1);
|
||||
}, 5000);
|
||||
} else {
|
||||
window.addEventListener("beforeunload", function() {
|
||||
document.getElementById('overlay').classList.add('loading')
|
||||
});
|
||||
}
|
||||
3
php/public/before-unload.js
Normal file
3
php/public/before-unload.js
Normal file
@@ -0,0 +1,3 @@
|
||||
window.addEventListener("beforeunload", function() {
|
||||
document.getElementById('overlay').classList.add('loading')
|
||||
});
|
||||
5
php/public/disable-clamav.js
Normal file
5
php/public/disable-clamav.js
Normal file
@@ -0,0 +1,5 @@
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
// Clamav
|
||||
var clamav = document.getElementById("clamav");
|
||||
clamav.disabled = true;
|
||||
});
|
||||
5
php/public/disable-collabora.js
Normal file
5
php/public/disable-collabora.js
Normal file
@@ -0,0 +1,5 @@
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
// Collabora
|
||||
var collabora = document.getElementById("collabora");
|
||||
collabora.disabled = true;
|
||||
});
|
||||
5
php/public/disable-onlyoffice.js
Normal file
5
php/public/disable-onlyoffice.js
Normal file
@@ -0,0 +1,5 @@
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
// OnlyOffice
|
||||
var onlyoffice = document.getElementById("onlyoffice");
|
||||
onlyoffice.disabled = true;
|
||||
});
|
||||
5
php/public/disable-talk.js
Normal file
5
php/public/disable-talk.js
Normal file
@@ -0,0 +1,5 @@
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
// Talk
|
||||
var talk = document.getElementById("talk");
|
||||
talk.disabled = true;
|
||||
});
|
||||
@@ -48,9 +48,11 @@ $app->add(new \AIO\Middleware\AuthMiddleware($container->get(\AIO\Auth\AuthManag
|
||||
|
||||
// API
|
||||
$app->post('/api/docker/watchtower', AIO\Controller\DockerController::class . ':StartWatchtowerContainer');
|
||||
$app->get('/api/docker/getwatchtower', AIO\Controller\DockerController::class . ':StartWatchtowerContainer');
|
||||
$app->post('/api/docker/start', AIO\Controller\DockerController::class . ':StartContainer');
|
||||
$app->post('/api/docker/backup', AIO\Controller\DockerController::class . ':StartBackupContainerBackup');
|
||||
$app->post('/api/docker/backup-check', AIO\Controller\DockerController::class . ':StartBackupContainerCheck');
|
||||
$app->post('/api/docker/backup-test', AIO\Controller\DockerController::class . ':StartBackupContainerTest');
|
||||
$app->post('/api/docker/restore', AIO\Controller\DockerController::class . ':StartBackupContainerRestore');
|
||||
$app->post('/api/docker/stop', AIO\Controller\DockerController::class . ':StopContainer');
|
||||
$app->get('/api/docker/logs', AIO\Controller\DockerController::class . ':GetLogs');
|
||||
@@ -72,18 +74,27 @@ $app->get('/containers', function ($request, $response, $args) use ($container)
|
||||
return $view->render($response, 'containers.twig', [
|
||||
'domain' => $configurationManager->GetDomain(),
|
||||
'borg_backup_host_location' => $configurationManager->GetBorgBackupHostLocation(),
|
||||
'borg_backup_mode' => $configurationManager->GetBorgBackupMode(),
|
||||
'nextcloud_password' => $configurationManager->GetSecret('NEXTCLOUD_PASSWORD'),
|
||||
'containers' => (new \AIO\ContainerDefinitionFetcher($container->get(\AIO\Data\ConfigurationManager::class), $container))->FetchDefinition(),
|
||||
'borgbackup_password' => $configurationManager->GetSecret('BORGBACKUP_PASSWORD'),
|
||||
'is_mastercontainer_update_available' => $dockerActionManger->IsMastercontainerUpdateAvailable(),
|
||||
'has_backup_run_once' => $configurationManager->hasBackupRunOnce(),
|
||||
'is_backup_container_running' => $dockerActionManger->isBackupContainerRunning(),
|
||||
'backup_exit_code' => $dockerActionManger->GetBackupcontainerExitCode(),
|
||||
'borg_backup_mode' => $configurationManager->GetBorgBackupMode(),
|
||||
'was_start_button_clicked' => $configurationManager->wasStartButtonClicked(),
|
||||
'has_update_available' => $dockerActionManger->isAnyUpdateAvailable(),
|
||||
'last_backup_time' => $configurationManager->GetLastBackupTime(),
|
||||
'backup_times' => $configurationManager->GetBackupTimes(),
|
||||
'current_channel' => $dockerActionManger->GetCurrentChannel(),
|
||||
'is_x64_platform' => $configurationManager->isx64Platform(),
|
||||
'is_clamav_enabled' => $configurationManager->isClamavEnabled(),
|
||||
'is_onlyoffice_enabled' => $configurationManager->isOnlyofficeEnabled(),
|
||||
'is_collabora_enabled' => $configurationManager->isCollaboraEnabled(),
|
||||
'is_talk_enabled' => $configurationManager->isTalkEnabled(),
|
||||
'borg_restore_password' => $configurationManager->GetBorgRestorePassword(),
|
||||
'daily_backup_time' => $configurationManager->GetDailyBackupTime(),
|
||||
'is_daily_backup_running' => $configurationManager->isDailyBackupRunning(),
|
||||
]);
|
||||
})->setName('profile');
|
||||
$app->get('/login', function ($request, $response, $args) use ($container) {
|
||||
@@ -138,4 +149,6 @@ $app->get('/', function (\Psr\Http\Message\RequestInterface $request, \Psr\Http\
|
||||
}
|
||||
});
|
||||
|
||||
$errorMiddleware = $app->addErrorMiddleware(true, true, true);
|
||||
|
||||
$app->run();
|
||||
|
||||
26
php/public/options-form-submit.js
Normal file
26
php/public/options-form-submit.js
Normal file
@@ -0,0 +1,26 @@
|
||||
function makeOptionsFormSubmitVisible() {
|
||||
var optionsFormSubmit = document.getElementById("options-form-submit");
|
||||
optionsFormSubmit.style.display = 'block';
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
// handle submit button for options form
|
||||
var optionsFormSubmit = document.getElementById("options-form-submit");
|
||||
optionsFormSubmit.style.display = 'none';
|
||||
|
||||
// Clamav
|
||||
var clamav = document.getElementById("clamav");
|
||||
clamav.addEventListener('change', makeOptionsFormSubmitVisible);
|
||||
|
||||
// OnlyOffice
|
||||
var onlyoffice = document.getElementById("onlyoffice");
|
||||
onlyoffice.addEventListener('change', makeOptionsFormSubmitVisible);
|
||||
|
||||
// Collabora
|
||||
var collabora = document.getElementById("collabora");
|
||||
collabora.addEventListener('change', makeOptionsFormSubmitVisible);
|
||||
|
||||
// Talk
|
||||
var talk = document.getElementById("talk");
|
||||
talk.addEventListener('change', makeOptionsFormSubmitVisible);
|
||||
});
|
||||
@@ -23,6 +23,10 @@ a {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
summary {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
|
||||
@@ -91,6 +91,10 @@ class Container {
|
||||
return $this->dockerActionManager->GetContainerRunningState($this);
|
||||
}
|
||||
|
||||
public function GetRestartingState() : IContainerState {
|
||||
return $this->dockerActionManager->GetContainerRestartingState($this);
|
||||
}
|
||||
|
||||
public function GetUpdateState() : IContainerState {
|
||||
return $this->dockerActionManager->GetContainerUpdateState($this);
|
||||
}
|
||||
|
||||
6
php/src/Container/State/NotRestartingState.php
Normal file
6
php/src/Container/State/NotRestartingState.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace AIO\Container\State;
|
||||
|
||||
class NotRestartingState implements IContainerState
|
||||
{}
|
||||
6
php/src/Container/State/RestartingState.php
Normal file
6
php/src/Container/State/RestartingState.php
Normal file
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
namespace AIO\Container\State;
|
||||
|
||||
class RestartingState implements IContainerState
|
||||
{}
|
||||
@@ -27,7 +27,7 @@ class ContainerDefinitionFetcher
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
public function GetContainerById(string $id): ?Container
|
||||
public function GetContainerById(string $id): Container
|
||||
{
|
||||
$containers = $this->FetchDefinition();
|
||||
|
||||
@@ -37,7 +37,7 @@ class ContainerDefinitionFetcher
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
throw new \Exception("The provided id " . $id . " was not found in the container definition.");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,6 +49,24 @@ class ContainerDefinitionFetcher
|
||||
|
||||
$containers = [];
|
||||
foreach ($data['production'] as $entry) {
|
||||
if ($entry['identifier'] === 'nextcloud-aio-clamav') {
|
||||
if (!$this->configurationManager->isClamavEnabled()) {
|
||||
continue;
|
||||
}
|
||||
} elseif ($entry['identifier'] === 'nextcloud-aio-onlyoffice') {
|
||||
if (!$this->configurationManager->isOnlyofficeEnabled()) {
|
||||
continue;
|
||||
}
|
||||
} elseif ($entry['identifier'] === 'nextcloud-aio-collabora') {
|
||||
if (!$this->configurationManager->isCollaboraEnabled()) {
|
||||
continue;
|
||||
}
|
||||
} elseif ($entry['identifier'] === 'nextcloud-aio-talk') {
|
||||
if (!$this->configurationManager->isTalkEnabled()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$ports = new ContainerPorts();
|
||||
foreach ($entry['ports'] as $port) {
|
||||
if($port === '%APACHE_PORT%/tcp') {
|
||||
@@ -99,6 +117,28 @@ class ContainerDefinitionFetcher
|
||||
);
|
||||
}
|
||||
|
||||
$dependsOn = [];
|
||||
foreach ($entry['dependsOn'] as $value) {
|
||||
if ($value === 'nextcloud-aio-clamav') {
|
||||
if (!$this->configurationManager->isClamavEnabled()) {
|
||||
continue;
|
||||
}
|
||||
} elseif ($value === 'nextcloud-aio-onlyoffice') {
|
||||
if (!$this->configurationManager->isOnlyofficeEnabled()) {
|
||||
continue;
|
||||
}
|
||||
} elseif ($value === 'nextcloud-aio-collabora') {
|
||||
if (!$this->configurationManager->isCollaboraEnabled()) {
|
||||
continue;
|
||||
}
|
||||
} elseif ($value === 'nextcloud-aio-talk') {
|
||||
if (!$this->configurationManager->isTalkEnabled()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$dependsOn[] = $value;
|
||||
}
|
||||
|
||||
$variables = new ContainerEnvironmentVariables();
|
||||
foreach ($entry['environmentVariables'] as $value) {
|
||||
$variables->AddVariable($value);
|
||||
@@ -114,7 +154,7 @@ class ContainerDefinitionFetcher
|
||||
$internalPorts,
|
||||
$volumes,
|
||||
$variables,
|
||||
$entry['dependsOn'],
|
||||
$dependsOn,
|
||||
$entry['secrets'],
|
||||
$this->container->get(DockerActionManager::class)
|
||||
);
|
||||
|
||||
@@ -22,7 +22,8 @@ class ConfigurationController
|
||||
public function SetConfig(Request $request, Response $response, $args) : Response {
|
||||
try {
|
||||
if (isset($request->getParsedBody()['domain'])) {
|
||||
$this->configurationManager->SetDomain($request->getParsedBody()['domain']);
|
||||
$domain = $request->getParsedBody()['domain'] ?? '';
|
||||
$this->configurationManager->SetDomain($domain);
|
||||
}
|
||||
|
||||
if (isset($request->getParsedBody()['current-master-password']) || isset($request->getParsedBody()['new-master-password'])) {
|
||||
@@ -32,7 +33,49 @@ class ConfigurationController
|
||||
}
|
||||
|
||||
if (isset($request->getParsedBody()['borg_backup_host_location'])) {
|
||||
$this->configurationManager->SetBorgBackupHostLocation($request->getParsedBody()['borg_backup_host_location']);
|
||||
$location = $request->getParsedBody()['borg_backup_host_location'] ?? '';
|
||||
$this->configurationManager->SetBorgBackupHostLocation($location);
|
||||
}
|
||||
|
||||
if (isset($request->getParsedBody()['borg_restore_host_location']) || isset($request->getParsedBody()['borg_restore_password'])) {
|
||||
$restoreLocation = $request->getParsedBody()['borg_restore_host_location'] ?? '';
|
||||
$borgPassword = $request->getParsedBody()['borg_restore_password'] ?? '';
|
||||
$this->configurationManager->SetBorgRestoreHostLocationAndPassword($restoreLocation, $borgPassword);
|
||||
}
|
||||
|
||||
if (isset($request->getParsedBody()['daily_backup_time'])) {
|
||||
$dailyBackupTime = $request->getParsedBody()['daily_backup_time'] ?? '';
|
||||
$this->configurationManager->SetDailyBackupTime($dailyBackupTime);
|
||||
}
|
||||
|
||||
if (isset($request->getParsedBody()['delete_daily_backup_time'])) {
|
||||
$this->configurationManager->DeleteDailyBackupTime();
|
||||
}
|
||||
|
||||
if (isset($request->getParsedBody()['options-form'])) {
|
||||
if (isset($request->getParsedBody()['collabora']) && isset($request->getParsedBody()['onlyoffice'])) {
|
||||
throw new InvalidSettingConfigurationException("Collabora and Onlyoffice are not allowed to be enabled at the same time!");
|
||||
}
|
||||
if (isset($request->getParsedBody()['clamav'])) {
|
||||
$this->configurationManager->SetClamavEnabledState(1);
|
||||
} else {
|
||||
$this->configurationManager->SetClamavEnabledState(0);
|
||||
}
|
||||
if (isset($request->getParsedBody()['onlyoffice'])) {
|
||||
$this->configurationManager->SetOnlyofficeEnabledState(1);
|
||||
} else {
|
||||
$this->configurationManager->SetOnlyofficeEnabledState(0);
|
||||
}
|
||||
if (isset($request->getParsedBody()['collabora'])) {
|
||||
$this->configurationManager->SetCollaboraEnabledState(1);
|
||||
} else {
|
||||
$this->configurationManager->SetCollaboraEnabledState(0);
|
||||
}
|
||||
if (isset($request->getParsedBody()['talk'])) {
|
||||
$this->configurationManager->SetTalkEnabledState(1);
|
||||
} else {
|
||||
$this->configurationManager->SetTalkEnabledState(0);
|
||||
}
|
||||
}
|
||||
|
||||
return $response->withStatus(201)->withHeader('Location', '/');
|
||||
|
||||
@@ -33,9 +33,19 @@ class DockerController
|
||||
$this->PerformRecursiveContainerStart($dependency);
|
||||
}
|
||||
|
||||
$pullcontainer = true;
|
||||
if ($id === 'nextcloud-aio-database') {
|
||||
if ($this->dockerActionManager->GetDatabasecontainerExitCode() > 0) {
|
||||
$pullcontainer = false;
|
||||
}
|
||||
}
|
||||
$this->dockerActionManager->DeleteContainer($container);
|
||||
$this->dockerActionManager->CreateVolumes($container);
|
||||
$this->dockerActionManager->PullContainer($container);
|
||||
if ($pullcontainer) {
|
||||
$this->dockerActionManager->PullContainer($container);
|
||||
} else {
|
||||
error_log('Not pulling the latest database image because the container was not correctly shut down.');
|
||||
}
|
||||
$this->dockerActionManager->CreateContainer($container);
|
||||
$this->dockerActionManager->StartContainer($container);
|
||||
$this->dockerActionManager->ConnectContainerToNetwork($container);
|
||||
@@ -44,8 +54,12 @@ class DockerController
|
||||
public function GetLogs(Request $request, Response $response, $args) : Response
|
||||
{
|
||||
$id = $request->getQueryParams()['id'];
|
||||
$container = $this->containerDefinitionFetcher->GetContainerById($id);
|
||||
$logs = $this->dockerActionManager->GetLogs($container);
|
||||
if (str_starts_with($id, 'nextcloud-aio-')) {
|
||||
$logs = $this->dockerActionManager->GetLogs($id);
|
||||
} else {
|
||||
$logs = 'Container not found.';
|
||||
}
|
||||
|
||||
$body = $response->getBody();
|
||||
$body->write($logs);
|
||||
|
||||
@@ -56,6 +70,11 @@ class DockerController
|
||||
}
|
||||
|
||||
public function StartBackupContainerBackup(Request $request, Response $response, $args) : Response {
|
||||
$this->startBackup();
|
||||
return $response->withStatus(201)->withHeader('Location', '/');
|
||||
}
|
||||
|
||||
public function startBackup() : void {
|
||||
$config = $this->configurationManager->GetConfig();
|
||||
$config['backup-mode'] = 'backup';
|
||||
$this->configurationManager->WriteConfig($config);
|
||||
@@ -65,8 +84,6 @@ class DockerController
|
||||
|
||||
$id = 'nextcloud-aio-borgbackup';
|
||||
$this->PerformRecursiveContainerStart($id);
|
||||
|
||||
return $response->withStatus(201)->withHeader('Location', '/');
|
||||
}
|
||||
|
||||
public function StartBackupContainerCheck(Request $request, Response $response, $args) : Response {
|
||||
@@ -95,6 +112,20 @@ class DockerController
|
||||
return $response->withStatus(201)->withHeader('Location', '/');
|
||||
}
|
||||
|
||||
public function StartBackupContainerTest(Request $request, Response $response, $args) : Response {
|
||||
$config = $this->configurationManager->GetConfig();
|
||||
$config['backup-mode'] = 'test';
|
||||
$this->configurationManager->WriteConfig($config);
|
||||
|
||||
$id = self::TOP_CONTAINER;
|
||||
$this->PerformRecursiveContainerStop($id);
|
||||
|
||||
$id = 'nextcloud-aio-borgbackup';
|
||||
$this->PerformRecursiveContainerStart($id);
|
||||
|
||||
return $response->withStatus(201)->withHeader('Location', '/');
|
||||
}
|
||||
|
||||
public function StartContainer(Request $request, Response $response, $args) : Response
|
||||
{
|
||||
$uri = $request->getUri();
|
||||
@@ -106,6 +137,16 @@ class DockerController
|
||||
$config['AIO_URL'] = $host . ':' . $port;
|
||||
// set wasStartButtonClicked
|
||||
$config['wasStartButtonClicked'] = 1;
|
||||
$this->configurationManager->WriteConfig($config);
|
||||
|
||||
// Start container
|
||||
$this->startTopContainer();
|
||||
|
||||
return $response->withStatus(201)->withHeader('Location', '/');
|
||||
}
|
||||
|
||||
public function startTopContainer() : void {
|
||||
$config = $this->configurationManager->GetConfig();
|
||||
// set AIO_TOKEN
|
||||
$config['AIO_TOKEN'] = bin2hex(random_bytes(24));
|
||||
$this->configurationManager->WriteConfig($config);
|
||||
@@ -116,14 +157,17 @@ class DockerController
|
||||
$id = self::TOP_CONTAINER;
|
||||
|
||||
$this->PerformRecursiveContainerStart($id);
|
||||
return $response->withStatus(201)->withHeader('Location', '/');
|
||||
}
|
||||
|
||||
public function StartWatchtowerContainer(Request $request, Response $response, $args) : Response {
|
||||
$this->startWatchtower();
|
||||
return $response->withStatus(201)->withHeader('Location', '/');
|
||||
}
|
||||
|
||||
public function startWatchtower() : void {
|
||||
$id = 'nextcloud-aio-watchtower';
|
||||
|
||||
$this->PerformRecursiveContainerStart($id);
|
||||
return $response->withStatus(201)->withHeader('Location', '/');
|
||||
}
|
||||
|
||||
private function PerformRecursiveContainerStop(string $id) : void
|
||||
@@ -149,22 +193,33 @@ class DockerController
|
||||
public function StartDomaincheckContainer() : void
|
||||
{
|
||||
# Don't start if domain is already set
|
||||
if ($this->configurationManager->GetDomain() != '') {
|
||||
if ($this->configurationManager->GetDomain() !== '' || $this->configurationManager->wasStartButtonClicked()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$id = 'nextcloud-aio-domaincheck';
|
||||
|
||||
$container = $this->containerDefinitionFetcher->GetContainerById($id);
|
||||
// don't start if the domaincheck is already running
|
||||
if ($container->GetIdentifier() === $id && $container->GetRunningState() instanceof RunningState) {
|
||||
return;
|
||||
// don't start if apache is already running
|
||||
} elseif ($container->GetIdentifier() === self::TOP_CONTAINER && $container->GetRunningState() instanceof RunningState) {
|
||||
$cacheKey = 'domaincheckWasStarted';
|
||||
|
||||
$domaincheckContainer = $this->containerDefinitionFetcher->GetContainerById($id);
|
||||
$apacheContainer = $this->containerDefinitionFetcher->GetContainerById(self::TOP_CONTAINER);
|
||||
// Don't start if apache is already running
|
||||
if ($apacheContainer->GetRunningState() instanceof RunningState) {
|
||||
return;
|
||||
// Don't start if domaincheck is already running
|
||||
} elseif ($domaincheckContainer->GetRunningState() instanceof RunningState) {
|
||||
$domaincheckWasStarted = apcu_fetch($cacheKey);
|
||||
// Start domaincheck again when 10 minutes are over by not returning here
|
||||
if($domaincheckWasStarted !== false && is_string($domaincheckWasStarted)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$this->StopDomaincheckContainer();
|
||||
$this->PerformRecursiveContainerStart($id);
|
||||
|
||||
// Cache the start for 10 minutes
|
||||
apcu_add($cacheKey, '1', 600);
|
||||
}
|
||||
|
||||
private function StopDomaincheckContainer() : void
|
||||
|
||||
29
php/src/Cron/BackupNotification.php
Normal file
29
php/src/Cron/BackupNotification.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
// increase memory limit to 2GB
|
||||
ini_set('memory_limit', '2048M');
|
||||
|
||||
use DI\Container;
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
$container = \AIO\DependencyInjection::GetContainer();
|
||||
|
||||
/** @var \AIO\Docker\DockerActionManager $dockerActionManger */
|
||||
$dockerActionManger = $container->get(\AIO\Docker\DockerActionManager::class);
|
||||
/** @var \AIO\ContainerDefinitionFetcher $containerDefinitionFetcher */
|
||||
$containerDefinitionFetcher = $container->get(\AIO\ContainerDefinitionFetcher::class);
|
||||
|
||||
$id = 'nextcloud-aio-nextcloud';
|
||||
$nextcloudContainer = $containerDefinitionFetcher->GetContainerById($id);
|
||||
|
||||
$backupExitCode = $dockerActionManger->GetBackupcontainerExitCode();
|
||||
|
||||
if ($backupExitCode === 0) {
|
||||
$dockerActionManger->sendNotification($nextcloudContainer, 'Daily backup successful!', 'You can get further info by looking at the backup logs in the AIO interface.');
|
||||
}
|
||||
|
||||
if ($backupExitCode > 0) {
|
||||
$dockerActionManger->sendNotification($nextcloudContainer, 'Daily backup failed!', 'You can get further info by looking at the backup logs in the AIO interface.');
|
||||
}
|
||||
20
php/src/Cron/DailyBackup.php
Normal file
20
php/src/Cron/DailyBackup.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
// increase memory limit to 2GB
|
||||
ini_set('memory_limit', '2048M');
|
||||
|
||||
use DI\Container;
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
$container = \AIO\DependencyInjection::GetContainer();
|
||||
|
||||
/** @var \AIO\Controller\DockerController $dockerController */
|
||||
$dockerController = $container->get(\AIO\Controller\DockerController::class);
|
||||
|
||||
// Stop container and start backup
|
||||
$dockerController->startBackup();
|
||||
|
||||
// Start apache
|
||||
$dockerController->startTopContainer();
|
||||
17
php/src/Cron/UpdateMastercontainer.php
Normal file
17
php/src/Cron/UpdateMastercontainer.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
// increase memory limit to 2GB
|
||||
ini_set('memory_limit', '2048M');
|
||||
|
||||
use DI\Container;
|
||||
|
||||
require __DIR__ . '/../../vendor/autoload.php';
|
||||
|
||||
$container = \AIO\DependencyInjection::GetContainer();
|
||||
|
||||
/** @var \AIO\Controller\DockerController $dockerController */
|
||||
$dockerController = $container->get(\AIO\Controller\DockerController::class);
|
||||
|
||||
# Update the mastercontainer
|
||||
$dockerController->startWatchtower();
|
||||
@@ -102,6 +102,9 @@ class ConfigurationManager
|
||||
}
|
||||
}
|
||||
|
||||
// Reverse the array to list newest backup first
|
||||
$backupTimes = array_reverse($backupTimes);
|
||||
|
||||
return $backupTimes;
|
||||
}
|
||||
|
||||
@@ -113,6 +116,74 @@ class ConfigurationManager
|
||||
}
|
||||
}
|
||||
|
||||
public function isx64Platform() : bool {
|
||||
if (php_uname('m') === 'x86_64') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function isClamavEnabled() : bool {
|
||||
$config = $this->GetConfig();
|
||||
if (isset($config['isClamavEnabled']) && $config['isClamavEnabled'] === 1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function SetClamavEnabledState(int $value) : void {
|
||||
$config = $this->GetConfig();
|
||||
$config['isClamavEnabled'] = $value;
|
||||
$this->WriteConfig($config);
|
||||
}
|
||||
|
||||
public function isOnlyofficeEnabled() : bool {
|
||||
$config = $this->GetConfig();
|
||||
if (isset($config['isOnlyofficeEnabled']) && $config['isOnlyofficeEnabled'] === 1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function SetOnlyofficeEnabledState(int $value) : void {
|
||||
$config = $this->GetConfig();
|
||||
$config['isOnlyofficeEnabled'] = $value;
|
||||
$this->WriteConfig($config);
|
||||
}
|
||||
|
||||
public function isCollaboraEnabled() : bool {
|
||||
$config = $this->GetConfig();
|
||||
if (isset($config['isCollaboraEnabled']) && $config['isCollaboraEnabled'] === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function SetCollaboraEnabledState(int $value) : void {
|
||||
$config = $this->GetConfig();
|
||||
$config['isCollaboraEnabled'] = $value;
|
||||
$this->WriteConfig($config);
|
||||
}
|
||||
|
||||
public function isTalkEnabled() : bool {
|
||||
$config = $this->GetConfig();
|
||||
if (isset($config['isTalkEnabled']) && $config['isTalkEnabled'] === 0) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public function SetTalkEnabledState(int $value) : void {
|
||||
$config = $this->GetConfig();
|
||||
$config['isTalkEnabled'] = $value;
|
||||
$this->WriteConfig($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidSettingConfigurationException
|
||||
*/
|
||||
@@ -166,6 +237,8 @@ class ConfigurationManager
|
||||
// Write domain
|
||||
$config = $this->GetConfig();
|
||||
$config['domain'] = $domain;
|
||||
// Reset the borg restore password when setting the domain
|
||||
$config['borg_restore_password'] = '';
|
||||
$this->WriteConfig($config);
|
||||
}
|
||||
|
||||
@@ -212,6 +285,7 @@ class ConfigurationManager
|
||||
$allowedPrefixes = [
|
||||
'/mnt/',
|
||||
'/media/',
|
||||
'/host_mnt/',
|
||||
];
|
||||
|
||||
$isValidPath = false;
|
||||
@@ -227,7 +301,7 @@ class ConfigurationManager
|
||||
}
|
||||
|
||||
if(!$isValidPath) {
|
||||
throw new InvalidSettingConfigurationException("The path must start with '/mnt/' or '/media/' or be equal to '/var/backups'.");
|
||||
throw new InvalidSettingConfigurationException("The path must start with '/mnt/', '/media/' or '/host_mnt/' or be equal to '/var/backups'.");
|
||||
}
|
||||
|
||||
|
||||
@@ -236,6 +310,33 @@ class ConfigurationManager
|
||||
$this->WriteConfig($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidSettingConfigurationException
|
||||
*/
|
||||
public function SetBorgRestoreHostLocationAndPassword(string $location, string $password) : void {
|
||||
if ($location === '') {
|
||||
throw new InvalidSettingConfigurationException("Please enter a path!");
|
||||
}
|
||||
|
||||
$isValidPath = false;
|
||||
if (str_starts_with($location, '/') && !str_ends_with($location, '/')) {
|
||||
$isValidPath = true;
|
||||
}
|
||||
|
||||
if(!$isValidPath) {
|
||||
throw new InvalidSettingConfigurationException("The path may start with '/mnt/', '/media/' or '/host_mnt/' or may be equal to '/var/backups'.");
|
||||
}
|
||||
|
||||
if ($password === '') {
|
||||
throw new InvalidSettingConfigurationException("Please enter the password!");
|
||||
}
|
||||
|
||||
$config = $this->GetConfig();
|
||||
$config['borg_backup_host_location'] = $location;
|
||||
$config['borg_restore_password'] = $password;
|
||||
$this->WriteConfig($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidSettingConfigurationException
|
||||
*/
|
||||
@@ -265,26 +366,10 @@ class ConfigurationManager
|
||||
}
|
||||
|
||||
public function GetApachePort() : string {
|
||||
$port = getenv('APACHE_PORT');
|
||||
if ($port === false) {
|
||||
$config = $this->GetConfig();
|
||||
if (!isset($config['apache_port']) || $config['apache_port'] === '') {
|
||||
$config['apache_port'] = '443';
|
||||
}
|
||||
return $config['apache_port'];
|
||||
} else {
|
||||
if(file_exists(DataConst::GetConfigFile())) {
|
||||
$config = $this->GetConfig();
|
||||
if (!isset($config['apache_port'])) {
|
||||
$config['apache_port'] = '';
|
||||
}
|
||||
if ($port !== $config['apache_port']) {
|
||||
$config['apache_port'] = $port;
|
||||
$this->WriteConfig($config);
|
||||
}
|
||||
}
|
||||
return $port;
|
||||
}
|
||||
$envVariableName = 'APACHE_PORT';
|
||||
$configName = 'apache_port';
|
||||
$defaultValue = '443';
|
||||
return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -297,6 +382,28 @@ class ConfigurationManager
|
||||
file_put_contents(DataConst::GetConfigFile(), json_encode($config));
|
||||
}
|
||||
|
||||
private function GetEnvironmentalVariableOrConfig(string $envVariableName, string $configName, string $defaultValue) : string {
|
||||
$envVariableOutput = getenv($envVariableName);
|
||||
if ($envVariableOutput === false) {
|
||||
$config = $this->GetConfig();
|
||||
if (!isset($config[$configName]) || $config[$configName] === '') {
|
||||
$config[$configName] = $defaultValue;
|
||||
}
|
||||
return $config[$configName];
|
||||
}
|
||||
if(file_exists(DataConst::GetConfigFile())) {
|
||||
$config = $this->GetConfig();
|
||||
if (!isset($config[$configName])) {
|
||||
$config[$configName] = '';
|
||||
}
|
||||
if ($envVariableOutput !== $config[$configName]) {
|
||||
$config[$configName] = $envVariableOutput;
|
||||
$this->WriteConfig($config);
|
||||
}
|
||||
}
|
||||
return $envVariableOutput;
|
||||
}
|
||||
|
||||
public function GetBorgBackupHostLocation() : string {
|
||||
$config = $this->GetConfig();
|
||||
if(!isset($config['borg_backup_host_location'])) {
|
||||
@@ -306,6 +413,15 @@ class ConfigurationManager
|
||||
return $config['borg_backup_host_location'];
|
||||
}
|
||||
|
||||
public function GetBorgRestorePassword() : string {
|
||||
$config = $this->GetConfig();
|
||||
if(!isset($config['borg_restore_password'])) {
|
||||
$config['borg_restore_password'] = '';
|
||||
}
|
||||
|
||||
return $config['borg_restore_password'];
|
||||
}
|
||||
|
||||
public function GetBorgBackupMode() : string {
|
||||
$config = $this->GetConfig();
|
||||
if(!isset($config['backup-mode'])) {
|
||||
@@ -316,48 +432,51 @@ class ConfigurationManager
|
||||
}
|
||||
|
||||
public function GetNextcloudMount() : string {
|
||||
$mount = getenv('NEXTCLOUD_MOUNT');
|
||||
if ($mount === false) {
|
||||
$config = $this->GetConfig();
|
||||
if (!isset($config['nextcloud_mount'])) {
|
||||
$config['nextcloud_mount'] = '';
|
||||
}
|
||||
return $config['nextcloud_mount'];
|
||||
} else {
|
||||
if(file_exists(DataConst::GetConfigFile())) {
|
||||
$config = $this->GetConfig();
|
||||
if (!isset($config['nextcloud_mount'])) {
|
||||
$config['nextcloud_mount'] = '';
|
||||
}
|
||||
if ($mount !== $config['nextcloud_mount']) {
|
||||
$config['nextcloud_mount'] = $mount;
|
||||
$this->WriteConfig($config);
|
||||
}
|
||||
}
|
||||
return $mount;
|
||||
}
|
||||
$envVariableName = 'NEXTCLOUD_MOUNT';
|
||||
$configName = 'nextcloud_mount';
|
||||
$defaultValue = '';
|
||||
return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue);
|
||||
}
|
||||
|
||||
public function GetNextcloudDatadirMount() : string {
|
||||
$mount = getenv('NEXTCLOUD_DATADIR');
|
||||
if ($mount === false) {
|
||||
$config = $this->GetConfig();
|
||||
if (!isset($config['nextcloud_datadir']) || $config['nextcloud_datadir'] === '') {
|
||||
$config['nextcloud_datadir'] = 'nextcloud_aio_nextcloud_data';
|
||||
}
|
||||
return $config['nextcloud_datadir'];
|
||||
} else {
|
||||
if(file_exists(DataConst::GetConfigFile())) {
|
||||
$config = $this->GetConfig();
|
||||
if (!isset($config['nextcloud_datadir'])) {
|
||||
$config['nextcloud_datadir'] = '';
|
||||
}
|
||||
if ($mount !== $config['nextcloud_datadir']) {
|
||||
$config['nextcloud_datadir'] = $mount;
|
||||
$this->WriteConfig($config);
|
||||
}
|
||||
}
|
||||
return $mount;
|
||||
$envVariableName = 'NEXTCLOUD_DATADIR';
|
||||
$configName = 'nextcloud_datadir';
|
||||
$defaultValue = 'nextcloud_aio_nextcloud_data';
|
||||
return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws InvalidSettingConfigurationException
|
||||
*/
|
||||
public function SetDailyBackupTime(string $time) : void {
|
||||
if ($time === "") {
|
||||
throw new InvalidSettingConfigurationException("The daily backup time must not be empty!");
|
||||
}
|
||||
|
||||
if (!preg_match("#^[0-1][0-9]:[0-5][0-9]$#", $time) && !preg_match("#^2[0-3]:[0-5][0-9]$#", $time)) {
|
||||
throw new InvalidSettingConfigurationException("You did not enter a correct time! One correct example is '04:00'!");
|
||||
}
|
||||
|
||||
file_put_contents(DataConst::GetDailyBackupTimeFile(), $time);
|
||||
}
|
||||
|
||||
public function GetDailyBackupTime() : string {
|
||||
if (!file_exists(DataConst::GetDailyBackupTimeFile())) {
|
||||
return '';
|
||||
}
|
||||
return file_get_contents(DataConst::GetDailyBackupTimeFile());
|
||||
}
|
||||
|
||||
public function DeleteDailyBackupTime() : void {
|
||||
if (file_exists(DataConst::GetDailyBackupTimeFile())) {
|
||||
unlink(DataConst::GetDailyBackupTimeFile());
|
||||
}
|
||||
}
|
||||
|
||||
public function isDailyBackupRunning() : bool {
|
||||
if (file_exists(DataConst::GetDailyBackupBlockFile())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,14 @@ class DataConst {
|
||||
return self::GetDataDirectory() . '/backupsecret';
|
||||
}
|
||||
|
||||
public static function GetDailyBackupTimeFile() : string {
|
||||
return self::GetDataDirectory() . '/daily_backup_time';
|
||||
}
|
||||
|
||||
public static function GetDailyBackupBlockFile() : string {
|
||||
return self::GetDataDirectory() . '/daily_backup_running';
|
||||
}
|
||||
|
||||
public static function GetBackupKeyFile() : string {
|
||||
return self::GetDataDirectory() . '/borg.config';
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ use AIO\Container\State\IContainerState;
|
||||
use AIO\Container\State\ImageDoesNotExistState;
|
||||
use AIO\Container\State\StartingState;
|
||||
use AIO\Container\State\RunningState;
|
||||
use AIO\Container\State\RestartingState;
|
||||
use AIO\Container\State\NotRestartingState;
|
||||
use AIO\Container\State\VersionDifferentState;
|
||||
use AIO\Container\State\StoppedState;
|
||||
use AIO\Container\State\VersionEqualState;
|
||||
@@ -70,18 +72,46 @@ class DockerActionManager
|
||||
}
|
||||
}
|
||||
|
||||
public function GetContainerRestartingState(Container $container) : IContainerState
|
||||
{
|
||||
$url = $this->BuildApiUrl(sprintf('containers/%s/json', urlencode($container->GetIdentifier())));
|
||||
try {
|
||||
$response = $this->guzzleClient->get($url);
|
||||
} catch (RequestException $e) {
|
||||
if ($e->getCode() === 404) {
|
||||
return new ImageDoesNotExistState();
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$responseBody = json_decode((string)$response->getBody(), true);
|
||||
|
||||
if ($responseBody['State']['Restarting'] === true) {
|
||||
return new RestartingState();
|
||||
} else {
|
||||
return new NotRestartingState();
|
||||
}
|
||||
}
|
||||
|
||||
public function GetContainerUpdateState(Container $container) : IContainerState
|
||||
{
|
||||
$tag = $this->GetCurrentChannel();
|
||||
|
||||
$runningDigest = $this->GetRepoDigestOfContainer($container->GetIdentifier());
|
||||
$remoteDigest = $this->dockerHubManager->GetLatestDigestOfTag($container->GetContainerName(), $tag);
|
||||
|
||||
if ($runningDigest === $remoteDigest || $remoteDigest === null || $runningDigest === null) {
|
||||
return new VersionEqualState();
|
||||
} else {
|
||||
$runningDigests = $this->GetRepoDigestsOfContainer($container->GetIdentifier());
|
||||
if ($runningDigests === null) {
|
||||
return new VersionDifferentState();
|
||||
}
|
||||
$remoteDigest = $this->dockerHubManager->GetLatestDigestOfTag($container->GetContainerName(), $tag);
|
||||
if ($remoteDigest === null) {
|
||||
return new VersionEqualstate();
|
||||
}
|
||||
|
||||
foreach($runningDigests as $runningDigest) {
|
||||
if ($runningDigest === $remoteDigest) {
|
||||
return new VersionEqualState();
|
||||
}
|
||||
}
|
||||
return new VersionDifferentState();
|
||||
}
|
||||
|
||||
public function GetContainerStartingState(Container $container) : IContainerState
|
||||
@@ -120,12 +150,12 @@ class DockerActionManager
|
||||
}
|
||||
}
|
||||
|
||||
public function GetLogs(Container $container) : string
|
||||
public function GetLogs(string $id) : string
|
||||
{
|
||||
$url = $this->BuildApiUrl(
|
||||
sprintf(
|
||||
'containers/%s/logs?stdout=true&stderr=true',
|
||||
urlencode($container->GetIdentifier())
|
||||
urlencode($id)
|
||||
));
|
||||
$responseBody = (string)$this->guzzleClient->get($url)->getBody();
|
||||
|
||||
@@ -216,6 +246,40 @@ class DockerActionManager
|
||||
$replacements[1] = $this->configurationManager->GetSelectedRestoreTime();
|
||||
} elseif ($out[1] === 'APACHE_PORT') {
|
||||
$replacements[1] = $this->configurationManager->GetApachePort();
|
||||
} elseif ($out[1] === 'NEXTCLOUD_MOUNT') {
|
||||
$replacements[1] = $this->configurationManager->GetNextcloudMount();
|
||||
} elseif ($out[1] === 'BACKUP_RESTORE_PASSWORD') {
|
||||
$replacements[1] = $this->configurationManager->GetBorgRestorePassword();
|
||||
} elseif ($out[1] === 'CLAMAV_ENABLED') {
|
||||
if ($this->configurationManager->isClamavEnabled()) {
|
||||
$replacements[1] = 'yes';
|
||||
} else {
|
||||
$replacements[1] = '';
|
||||
}
|
||||
} elseif ($out[1] === 'ONLYOFFICE_ENABLED') {
|
||||
if ($this->configurationManager->isOnlyofficeEnabled()) {
|
||||
$replacements[1] = 'yes';
|
||||
} else {
|
||||
$replacements[1] = '';
|
||||
}
|
||||
} elseif ($out[1] === 'COLLABORA_ENABLED') {
|
||||
if ($this->configurationManager->isCollaboraEnabled()) {
|
||||
$replacements[1] = 'yes';
|
||||
} else {
|
||||
$replacements[1] = '';
|
||||
}
|
||||
} elseif ($out[1] === 'TALK_ENABLED') {
|
||||
if ($this->configurationManager->isTalkEnabled()) {
|
||||
$replacements[1] = 'yes';
|
||||
} else {
|
||||
$replacements[1] = '';
|
||||
}
|
||||
} elseif ($out[1] === 'DAILY_BACKUP_RUNNING') {
|
||||
if ($this->configurationManager->isDailyBackupRunning()) {
|
||||
$replacements[1] = 'yes';
|
||||
} else {
|
||||
$replacements[1] = '';
|
||||
}
|
||||
} else {
|
||||
$replacements[1] = $this->configurationManager->GetSecret($out[1]);
|
||||
}
|
||||
@@ -250,30 +314,30 @@ class DockerActionManager
|
||||
}
|
||||
|
||||
$url = $this->BuildApiUrl('containers/create?name=' . $container->GetIdentifier());
|
||||
$this->guzzleClient->request(
|
||||
'POST',
|
||||
$url,
|
||||
[
|
||||
'json' => $requestBody
|
||||
]
|
||||
);
|
||||
try {
|
||||
$this->guzzleClient->request(
|
||||
'POST',
|
||||
$url,
|
||||
[
|
||||
'json' => $requestBody
|
||||
]
|
||||
);
|
||||
} catch (RequestException $e) {
|
||||
throw $e;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function PullContainer(Container $container) : void
|
||||
{
|
||||
$pullcontainer = true;
|
||||
if ($container->GetIdentifier() === 'nextcloud-aio-database') {
|
||||
if ($this->GetDatabasecontainerExitCode() > 0) {
|
||||
$pullcontainer = false;
|
||||
}
|
||||
}
|
||||
if ($pullcontainer) {
|
||||
$url = $this->BuildApiUrl(sprintf('images/create?fromImage=%s', urlencode($this->BuildImageName($container))));
|
||||
try {
|
||||
$this->guzzleClient->post($url);
|
||||
} catch (RequestException $e) {
|
||||
throw $e;
|
||||
}
|
||||
$url = $this->BuildApiUrl(sprintf('images/create?fromImage=%s', urlencode($this->BuildImageName($container))));
|
||||
try {
|
||||
$this->guzzleClient->post($url);
|
||||
} catch (RequestException $e) {
|
||||
error_log('Could not get image ' . $this->BuildImageName($container) . ' from docker hub. Probably due to rate limits. ' . $e->getMessage());
|
||||
// Don't exit here because it is possible that the image is already present
|
||||
// and we ran into docker hub limits.
|
||||
// We will exit later if not image should be available.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -301,7 +365,7 @@ class DockerActionManager
|
||||
}
|
||||
}
|
||||
|
||||
private function GetRepoDigestOfContainer(string $containerName) : ?string {
|
||||
private function GetRepoDigestsOfContainer(string $containerName) : ?array {
|
||||
try {
|
||||
$containerUrl = $this->BuildApiUrl(sprintf('containers/%s/json', $containerName));
|
||||
$containerOutput = json_decode($this->guzzleClient->get($containerUrl)->getBody()->getContents(), true);
|
||||
@@ -310,10 +374,30 @@ class DockerActionManager
|
||||
$imageUrl = $this->BuildApiUrl(sprintf('images/%s/json', $imageName));
|
||||
$imageOutput = json_decode($this->guzzleClient->get($imageUrl)->getBody()->getContents(), true);
|
||||
|
||||
if(isset($imageOutput['RepoDigests']) && count($imageOutput['RepoDigests']) === 1) {
|
||||
$fullDigest = $imageOutput['RepoDigests'][0];
|
||||
if (!isset($imageOutput['RepoDigests'])) {
|
||||
error_log('RepoDigests is not set of container ' . $containerName);
|
||||
return null;
|
||||
}
|
||||
|
||||
return substr($fullDigest, strpos($fullDigest, "@") + 1);
|
||||
if (!is_array($imageOutput['RepoDigests'])) {
|
||||
error_log('RepoDigests of ' . $containerName . ' is not an array which is not allowed!');
|
||||
return null;
|
||||
}
|
||||
|
||||
$repoDigestArray = [];
|
||||
$oneDigestGiven = false;
|
||||
foreach($imageOutput['RepoDigests'] as $repoDigest) {
|
||||
$digestPosition = strpos($repoDigest, '@');
|
||||
if ($digestPosition === false) {
|
||||
error_log('Somehow the RepoDigest of ' . $containerName . ' does not contain a @.');
|
||||
return null;
|
||||
}
|
||||
$repoDigestArray[] = substr($repoDigest, $digestPosition + 1);
|
||||
$oneDigestGiven = true;
|
||||
}
|
||||
|
||||
if ($oneDigestGiven) {
|
||||
return $repoDigestArray;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -339,6 +423,7 @@ class DockerActionManager
|
||||
apcu_add($cacheKey, $tag);
|
||||
return $tag;
|
||||
} catch (\Exception $e) {
|
||||
error_log('Could not get current channel ' . $e->getMessage());
|
||||
}
|
||||
|
||||
return 'latest';
|
||||
@@ -351,14 +436,21 @@ class DockerActionManager
|
||||
|
||||
$tag = $this->GetCurrentChannel();
|
||||
|
||||
$runningDigest = $this->GetRepoDigestOfContainer($containerName);
|
||||
$remoteDigest = $this->dockerHubManager->GetLatestDigestOfTag($imageName, $tag);
|
||||
|
||||
if ($remoteDigest === $runningDigest || $remoteDigest === null || $runningDigest === null) {
|
||||
return false;
|
||||
} else {
|
||||
$runningDigests = $this->GetRepoDigestsOfContainer($containerName);
|
||||
if ($runningDigests === null) {
|
||||
return true;
|
||||
}
|
||||
$remoteDigest = $this->dockerHubManager->GetLatestDigestOfTag($imageName, $tag);
|
||||
if ($remoteDigest === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($runningDigests as $runningDigest) {
|
||||
if ($remoteDigest === $runningDigest) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function sendNotification(Container $container, string $subject, string $message) : void
|
||||
@@ -425,6 +517,7 @@ class DockerActionManager
|
||||
]
|
||||
);
|
||||
} catch (RequestException $e) {
|
||||
error_log('Could not disconnect container from network ' . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,4 +639,13 @@ class DockerActionManager
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public function isBackupContainerRunning() : bool {
|
||||
$id = 'nextcloud-aio-borgbackup';
|
||||
$backupContainer = $this->containerDefinitionFetcher->GetContainerById($id);
|
||||
if ($this->GetContainerRunningState($backupContainer) instanceof RunningState) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ class DockerHubManager
|
||||
return $cachedVersion;
|
||||
}
|
||||
|
||||
// If one of the links below should ever become outdated, we can still upgrade the mastercontainer via the webinterface manually by opening '/api/docker/getwatchtower'
|
||||
|
||||
try {
|
||||
$authTokenRequest = $this->guzzleClient->request(
|
||||
'GET',
|
||||
@@ -50,8 +52,10 @@ class DockerHubManager
|
||||
}
|
||||
}
|
||||
|
||||
error_log('Could not get digest of container ' . $name . ':' . $tag);
|
||||
return null;
|
||||
} catch (\Exception $e) {
|
||||
error_log('Could not get digest of container ' . $name . ':' . $tag . ' ' . $e->getMessage());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,42 +16,56 @@
|
||||
</header>
|
||||
|
||||
<div class="content">
|
||||
<h1>Nextcloud AIO Beta v0.7.0</h1>
|
||||
This is beta software and not production ready.<br><br>
|
||||
<h1>Nextcloud AIO v1.0.1</h1>
|
||||
|
||||
{% set isAnyRunning = false %}
|
||||
{% set isAnyRestarting = false %}
|
||||
{% set isWatchtowerRunning = false %}
|
||||
{% set isBackupContainerRunning = false %}
|
||||
{% set isRestoreRunning = false %}
|
||||
{% set isBackupOrRestoreRunning = false %}
|
||||
{% set isApacheStarting = false %}
|
||||
|
||||
{% if is_backup_container_running == true %}
|
||||
{% if borg_backup_mode == 'restore' %}
|
||||
{% set isRestoreRunning = true %}
|
||||
{% endif %}
|
||||
{% if borg_backup_mode == 'backup' or borg_backup_mode == 'restore' %}
|
||||
{% set isBackupOrRestoreRunning = true %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% for container in containers %}
|
||||
{% if class(container.GetRunningState()) == 'AIO\\Container\\State\\RunningState' and container.GetIdentifier() != 'nextcloud-aio-domaincheck' and container.GetIdentifier() != 'nextcloud-aio-borgbackup' and container.GetIdentifier() != 'nextcloud-aio-watchtower' %}
|
||||
{% if container.GetIdentifier() not in ['nextcloud-aio-domaincheck', 'nextcloud-aio-borgbackup', 'nextcloud-aio-watchtower'] and class(container.GetRunningState()) == 'AIO\\Container\\State\\RunningState' %}
|
||||
{% set isAnyRunning = true %}
|
||||
{% endif %}
|
||||
{% if container.GetIdentifier() not in ['nextcloud-aio-domaincheck', 'nextcloud-aio-borgbackup', 'nextcloud-aio-watchtower'] and class(container.GetRestartingState()) == 'AIO\\Container\\State\\RestartingState' %}
|
||||
{% set isAnyRestarting = true %}
|
||||
{% endif %}
|
||||
{% if container.GetIdentifier() == 'nextcloud-aio-watchtower' and class(container.GetRunningState()) == 'AIO\\Container\\State\\RunningState' %}
|
||||
{% set isWatchtowerRunning = true %}
|
||||
{% endif %}
|
||||
{% if container.GetIdentifier() == 'nextcloud-aio-apache' and class(container.GetStartingState()) == 'AIO\\Container\\State\\StartingState' %}
|
||||
{% set isApacheStarting = true %}
|
||||
{% endif %}
|
||||
{% if container.GetIdentifier() == 'nextcloud-aio-borgbackup' and class(container.GetRunningState()) == 'AIO\\Container\\State\\RunningState' %}
|
||||
{% set isBackupContainerRunning = true %}
|
||||
{% if borg_backup_mode == 'restore' %}
|
||||
{% set isRestoreRunning = true %}
|
||||
{% endif %}
|
||||
{% if borg_backup_mode == 'backup' or borg_backup_mode == 'restore' %}
|
||||
{% set isBackupOrRestoreRunning = true %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if isWatchtowerRunning == true %}
|
||||
Mastercontainer update currently running. It will restart the mastercontainer soon which will make it unavailable for a moment. Please wait until that's done.<br /><br />
|
||||
{% if is_daily_backup_running == true %}
|
||||
<span class="status running"></span> Daily backup currently running. (<a href="/api/docker/logs?id=nextcloud-aio-mastercontainer">Logs</a>)<br /><br />
|
||||
It will update all containers and all apps if the backup is successful.<br /><br />
|
||||
{% if is_mastercontainer_update_available == true %}
|
||||
Since the mastercontainer gets updated, it will restart the container which will make it unavailable for a moment. (<a href="/api/docker/logs?id=nextcloud-aio-watchtower">Logs</a>)<br /><br />
|
||||
{% endif %}
|
||||
{% if has_update_available == false %}
|
||||
The whole process should not take more than a few minutes.<br /><br />
|
||||
{% else %}
|
||||
The whole process can take a while because your containers get updated.<br /><br />
|
||||
{% endif %}
|
||||
<a href="" class="button reload">Reload ↻</a><br/>
|
||||
{% elseif isWatchtowerRunning == true %}
|
||||
<span class="status running"></span> Mastercontainer update currently running. It will restart the mastercontainer soon which will make it unavailable for a moment. Please wait until that's done. (<a href="/api/docker/logs?id=nextcloud-aio-watchtower">Logs</a>)<br /><br />
|
||||
<a href="" class="button reload">Reload ↻</a><br/>
|
||||
{% else %}
|
||||
{% if isBackupOrRestoreRunning == false and domain == "" %}
|
||||
{% if is_backup_container_running == false and domain == "" %}
|
||||
{% if is_mastercontainer_update_available == true %}
|
||||
<h2>Mastercontainer update</h2>
|
||||
⚠ A mastercontainer update is available. Please click on the button below to update it. Afterwards, you will be able to proceed with the setup.<br><br>
|
||||
@@ -61,29 +75,118 @@
|
||||
<input class="button" type="submit" value="Update mastercontainer" />
|
||||
</form>
|
||||
{% else %}
|
||||
Please type in the domain that will be used for Nextcloud:<br><br />
|
||||
<form method="POST" action="/api/configuration" class="xhr">
|
||||
<input type="text" name="domain" value="{{ domain }}" placeholder="nextcloud.yourdomain.com"/>
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<input class="button" type="submit" value="Submit" />
|
||||
</form>
|
||||
Make sure that this server is reachable on Port 443 and you've correctly set up the DNS config for the domain that you enter. <br><br>
|
||||
If you have a dynamic IP-address, you can use e.g. <a href="https://ddclient.net/">DDclient</a> with a compatible domain provider for DNS updates.
|
||||
{% if borg_backup_host_location == '' and borg_restore_password == '' %}
|
||||
Nextcloud AIO stands for Nextcloud All In One and provides easy deployment and maintenance with most features included in this one Nextcloud instance.<br><br>
|
||||
<h2>New AIO instance</h2>
|
||||
Please type in the domain that will be used for Nextcloud if you want to create a new instance:<br><br />
|
||||
<form method="POST" action="/api/configuration" class="xhr">
|
||||
<input type="text" name="domain" value="{{ domain }}" placeholder="nextcloud.yourdomain.com"/>
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<input class="button" type="submit" value="Submit" />
|
||||
</form>
|
||||
Make sure that this server is reachable on Port 443 and you've correctly set up the DNS config for the domain that you enter. <br><br>
|
||||
If you have a dynamic IP-address, you can use e.g. <a href="https://ddclient.net/">DDclient</a> with a compatible domain provider for DNS updates. <br /><br/>
|
||||
|
||||
<h2>Restore AIO instance from backup</h2>
|
||||
You can alternatively restore an AIO instance from backup.<br><br>
|
||||
{% endif %}
|
||||
|
||||
{% if borg_backup_host_location != '' and borg_restore_password != '' %}
|
||||
{% if borg_backup_mode in ['test', 'check'] %}
|
||||
{% if backup_exit_code > 0 %}
|
||||
<span class="status error"></span> Last {{ borg_backup_mode }} failed! (<a href="/api/docker/logs?id=nextcloud-aio-borgbackup">Logs</a>)<br /><br />
|
||||
{% if borg_backup_mode == 'test' %}
|
||||
Please adjust the path and/or password in order to make it work! After changing and submitting the values, click on 'Test path and password' button at the bottom of this page to verify and test the new settings!<br><br>
|
||||
{% elseif borg_backup_mode == 'check' %}
|
||||
The backup archive seems to be corrupt. Please try to use a different intact backup archive or try to fix it by following <a href="https://borgbackup.readthedocs.io/en/stable/faq.html?highlight=repair#:~:text=repairing%20a%20damaged%20repository"><b>this documentation</b></a>
|
||||
{% endif %}
|
||||
{% elseif backup_exit_code == 0 %}
|
||||
<span class="status success"></span> Last {{ borg_backup_mode }} successful! (<a href="/api/docker/logs?id=nextcloud-aio-borgbackup">Logs</a>)<br /><br />
|
||||
{% if borg_backup_mode == 'test' %}
|
||||
Feel free to check the integrity of the backup archive below before starting the restore process in order to make double-sure that the restore will work. This can take a long time though depending on the size of the backup archive and is thus not required.<br><br>
|
||||
<form method="POST" action="/api/docker/backup-check" class="xhr">
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<input class="button" type="submit" value="Check backup integrity"/><br/>
|
||||
</form>
|
||||
{% endif %}
|
||||
Choose the backup that you want to restore and click on the button below to restore the selected backup. This will restore the whole AIO instance from backup. Please not that the current AIO password will be kept and the AIO password not restored from backup!<br><br>
|
||||
<form method="POST" action="/api/docker/restore" class="xhr" id="restore_selection">
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<select id="selected_restore_time" name="selected_restore_time" form="restore_selection">
|
||||
{% for restore_time in backup_times %}
|
||||
<option value="{{ restore_time }}">{{ restore_time }} UTC</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input class="button" type="submit" value="Restore selected backup"/>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% elseif borg_backup_mode == 'restore' %}
|
||||
{% if backup_exit_code > 0 %}
|
||||
<span class="status error"></span> Last restore failed! (<a href="/api/docker/logs?id=nextcloud-aio-borgbackup">Logs</a>)<br /><br />
|
||||
Somehow the restore failed which is unexpected! Please adjust the path and password, test it and try to restore again!
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if borg_backup_host_location == '' or borg_restore_password == '' or borg_backup_mode not in ['test', 'check', ''] or backup_exit_code > 0 %}
|
||||
Please enter the location of the backup archive on your host and the password of the backup archive below:<br><br>
|
||||
<form method="POST" action="/api/configuration" class="xhr">
|
||||
<input type="text" name="borg_restore_host_location" value="{{borg_backup_host_location}}" placeholder="/mnt/backup"/>
|
||||
<input type="text" name="borg_restore_password" value="{{borg_restore_password}}" placeholder="enter the borg password"/>
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<input class="button" type="submit" value="Submit" />
|
||||
</form>
|
||||
The folder path that you enter may start with <b>/mnt/</b>, <b>/media/</b> or <b>/host_mnt/</b> or may be equal to <b>/var/backups</b>.<br><br>So e.g. <b>/mnt/backup</b> on Linux and macOS or <b>/host_mnt/c/backup/directory</b> on Windows. (This Windows example would be equivalent to 'C:\backup\directory' on the Windows host. So you need to translate the path that you want to use into the correct format.)<br><br>
|
||||
⚠ Note that the backup archive must be located in a subfolder of the folder that you enter here and the subfolder which contains the archive must be named 'borg'. Otherwise will the backup container not find the backup archive!<br><br>
|
||||
{% endif %}
|
||||
{% if borg_backup_host_location != '' and borg_restore_password != '' %}
|
||||
{% if borg_backup_mode not in ['test', 'check'] or backup_exit_code != 0 %}
|
||||
<b>Everything set!</b> Click on the button below to test the path and password:<br/><br/>
|
||||
<form method="POST" action="/api/docker/backup-test" class="xhr">
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<input class="button" type="submit" value="Test path and password"/><br/>
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if domain != "" %}
|
||||
{% if domain != "" and was_start_button_clicked == true %}
|
||||
You are running the <a href="https://github.com/nextcloud/all-in-one#how-to-switch-the-channel"><b>{{ current_channel }}</b></a> channel. (<a href="/api/docker/logs?id=nextcloud-aio-mastercontainer">Logs</a>)<br><br>
|
||||
{% endif %}
|
||||
|
||||
{% if is_backup_container_running == true %}
|
||||
<span class="status running"></span> Backup container is currently running. (<a href="/api/docker/logs?id=nextcloud-aio-borgbackup">Logs</a>)<br /><br />
|
||||
<a href="" class="button reload">Reload ↻</a><br/><br>
|
||||
{% endif %}
|
||||
|
||||
{% if domain != "" %}
|
||||
{% if isAnyRunning == true %}
|
||||
{% if isApacheStarting != true %}
|
||||
Your initial Nextcloud credentials:<br><br />
|
||||
Initial Nextcloud username: admin<br />
|
||||
Initial Nextcloud password: {{ nextcloud_password }}<br /><br/>
|
||||
<details>
|
||||
<summary>Click here to reveal the initial Nextcloud credentials</summary><br />
|
||||
Initial Nextcloud username: <b>admin</b><br />
|
||||
Initial Nextcloud password: <b>{{ nextcloud_password }}</b>
|
||||
</details><br /><br />
|
||||
<a href="https://{{ domain }}" class="button" target="_blank" rel="noopener">Open your Nextcloud ↗</a><br/>
|
||||
{% else %}
|
||||
Containers are currently starting.<br /><br />
|
||||
<a href="" class="button reload">Reload ↻</a><br/>
|
||||
{% if isAnyRestarting == false %}
|
||||
<span class="status running"></span> Containers are currently starting.<br /><br />
|
||||
<a href="" class="button reload">Reload ↻</a><br/><br>
|
||||
{% else %}
|
||||
It seems like at least one container is currently restarting which means it is not able to start correctly.<br><br>
|
||||
To break out this endless loop, you can stop the containers below and investigate the issue by having a look at the container logs before starting them again.<br><br>
|
||||
<form method="POST" action="/api/docker/stop" class="xhr">
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<input class="button" type="submit" value="Stop containers" />
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
@@ -92,7 +195,7 @@
|
||||
<ul>
|
||||
{# @var containers \AIO\Container\Container[] #}
|
||||
{% for container in containers %}
|
||||
{% if container.GetIdentifier() != 'nextcloud-aio-borgbackup' and container.GetIdentifier() != 'nextcloud-aio-watchtower' and container.GetIdentifier() != 'nextcloud-aio-domaincheck' %}
|
||||
{% if container.GetIdentifier() not in ['nextcloud-aio-borgbackup', 'nextcloud-aio-watchtower', 'nextcloud-aio-domaincheck'] %}
|
||||
<li>
|
||||
{% if class(container.GetStartingState()) == 'AIO\\Container\\State\\StartingState' %}
|
||||
<span class="status running"></span>
|
||||
@@ -111,7 +214,7 @@
|
||||
|
||||
{% if has_update_available == true %}
|
||||
{% if is_mastercontainer_update_available == false %}
|
||||
⚠ Container updates are available. Click on `Stop Containers` and `Start Containers` to update them. You should consider creating a backup first.<br><br>
|
||||
⚠ Container updates are available. Click on 'Stop Containers' and 'Start Containers' to update them. You should consider creating a backup first.<br><br>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if is_mastercontainer_update_available == false %}
|
||||
@@ -124,6 +227,13 @@
|
||||
{% if isApacheStarting != true %}
|
||||
{% if is_mastercontainer_update_available == true %}
|
||||
⚠ A mastercontainer update is available. Please click on the button below to stop your containers in order to be able to update the mastercontainer.<br /><br />
|
||||
{% if current_channel starts with 'latest' %}
|
||||
You can find the changelog <a href="https://github.com/nextcloud/all-in-one/releases/latest"><b>here</b></a><br><br>
|
||||
{% elseif current_channel starts with 'beta' %}
|
||||
You can find the changelog <a href="https://github.com/nextcloud/all-in-one/releases"><b>here</b></a><br><br>
|
||||
{% elseif current_channel starts with 'develop' %}
|
||||
You can find all changes <a href="https://github.com/nextcloud-releases/all-in-one/commits/main"><b>here</b></a><br><br>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<form method="POST" action="/api/docker/stop" class="xhr">
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
@@ -141,7 +251,12 @@
|
||||
Clicking on the button below will download all docker containers and start them. This can take a lot of time depending on your internect connection. Since the overall size is a few GB, this will take around 5-10 min or more. So be aware and patient!<br><br>
|
||||
{% endif %}
|
||||
{% if is_mastercontainer_update_available == true %}
|
||||
⚠ Please update your mastercontainer. Afterwards, you will be able to start your containers again.<br><br>
|
||||
⚠ A mastercontainer update is available. Please click on the button below to update it.<br><br>
|
||||
<form method="POST" action="/api/docker/watchtower" class="xhr">
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<input class="button" type="submit" value="Update mastercontainer" />
|
||||
</form>
|
||||
{% else %}
|
||||
{% if was_start_button_clicked == false or has_update_available == false %}
|
||||
<form method="POST" action="/api/docker/start" class="xhr">
|
||||
@@ -161,78 +276,49 @@
|
||||
{% endif %}
|
||||
|
||||
{% if was_start_button_clicked == true %}
|
||||
|
||||
{% if isBackupOrRestoreRunning == false %}
|
||||
<h2>Mastercontainer</h2>
|
||||
You are currently running the {{ current_channel }} channel.<br><br>
|
||||
|
||||
{% if is_mastercontainer_update_available == true %}
|
||||
{% if isAnyRunning == false %}
|
||||
⚠ A mastercontainer update is available. Please click on the button below to update it.<br><br>
|
||||
<form method="POST" action="/api/docker/watchtower" class="xhr">
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<input class="button" type="submit" value="Update mastercontainer" />
|
||||
</form>
|
||||
{% else %}
|
||||
⚠ A mastercontainer update is available. Please stop your containers in order to be able to update the mastercontainer.<br><br>
|
||||
{% endif %}
|
||||
{% if current_channel starts with 'latest' %}
|
||||
You can find the changelog <a href="https://github.com/nextcloud/all-in-one/releases/latest">here</a><br><br>
|
||||
{% elseif current_channel starts with 'beta' %}
|
||||
You can find the changelog <a href="https://github.com/nextcloud/all-in-one/releases">here</a><br><br>
|
||||
{% elseif current_channel starts with 'develop' %}
|
||||
You can find all changes <a href="https://github.com/nextcloud-releases/all-in-one/commits/main">here</a><br><br>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if isApacheStarting == false %}
|
||||
You can change your AIO password below:<br><br />
|
||||
<form method="POST" action="/api/configuration" class="xhr">
|
||||
<input type="text" autocomplete="current-password" name="current-master-password" placeholder="your current aio password"/>
|
||||
<input type="text" autocomplete="new-password" name="new-master-password" placeholder="your new aio password"/>
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<input class="button" type="submit" value="Submit" />
|
||||
</form>
|
||||
The new password needs to be at least 24 characters long. Allowed characters are the <a href="https://en.wikipedia.org/wiki/Latin_alphabet#/media/File:Abecedarium.png">latin characters</a> 'a-z', 'A-Z', '0-9' and spaces.<br><br>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if isBackupOrRestoreRunning == false and borg_backup_host_location == "" and isApacheStarting != true %}
|
||||
{% if is_backup_container_running == false and borg_backup_host_location == "" and isApacheStarting != true %}
|
||||
<h2>Backup and restore</h2>
|
||||
Please type in the directory where backups will get created on the host system:<br><br>
|
||||
<form method="POST" action="/api/configuration" class="xhr">
|
||||
<input type="text" name="borg_backup_host_location" value="/mnt/backup" placeholder="/mnt/backup"/>
|
||||
<input type="text" name="borg_backup_host_location" placeholder="/mnt/backup"/>
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<input class="button" type="submit" value="Submit" />
|
||||
</form>
|
||||
The folder path that you enter must start with <b>/mnt/</b> or <b>/media/</b> or be equal to <b>/var/backups</b>. So e.g. <b>/mnt/backup</b> or <b>/var/backups</b>
|
||||
The folder path that you enter must start with <b>/mnt/</b>, <b>/media/</b> or <b>/host_mnt/</b> or be equal to <b>/var/backups</b>.<br><br>So e.g. <b>/mnt/backup</b> on Linux and macOS or <b>/host_mnt/c/backup/directory</b> on Windows. (This Windows example would be equivalent to 'C:\backup\directory' on the Windows host. So you need to translate the path that you want to use into the correct format.)
|
||||
{% endif %}
|
||||
|
||||
{% if borg_backup_host_location != "" %}
|
||||
<h2>Backup and restore</h2>
|
||||
{% if isBackupContainerRunning == false %}
|
||||
{% if is_backup_container_running == false %}
|
||||
<h2>Backup and restore</h2>
|
||||
{% if backup_exit_code > 0 %}
|
||||
<span class="status error"></span> Last {{ borg_backup_mode }} failed! (<a href="/api/docker/logs?id=nextcloud-aio-borgbackup">Logs</a>)<br /><br />
|
||||
{% elseif backup_exit_code == 0 %}
|
||||
{% if borg_backup_mode == "backup" %}
|
||||
<span class="status success"></span> Last {{ borg_backup_mode }} successful on {{ last_backup_time }}! (<a href="/api/docker/logs?id=nextcloud-aio-borgbackup">Logs</a>)<br /><br />
|
||||
<span class="status success"></span> Last {{ borg_backup_mode }} successful on {{ last_backup_time }} UTC! (<a href="/api/docker/logs?id=nextcloud-aio-borgbackup">Logs</a>)<br /><br />
|
||||
{% else %}
|
||||
<span class="status success"></span> Last {{ borg_backup_mode }} successful! (<a href="/api/docker/logs?id=nextcloud-aio-borgbackup">Logs</a>)<br /><br />
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if isBackupContainerRunning == false %}
|
||||
This is your encryption password for backups: {{ borgbackup_password }} <br /><br/>
|
||||
{% if is_backup_container_running == false and isApacheStarting == false %}
|
||||
{% if has_backup_run_once == true %}
|
||||
<details>
|
||||
<summary>Click here to reveal all backup options</summary><br />
|
||||
{% endif %}
|
||||
<h3>Backup information</h3>
|
||||
This is your encryption password for backups: <b>{{ borgbackup_password }}</b><br /><br/>
|
||||
Please save it at a safe place since you won't be able to restore from backup if you loose this password! <br /><br/>
|
||||
Backed up will get all important data of your Nextcloud AIO instance like the database, your files and configuration files of the mastercontainer and else. <br /><br/>
|
||||
The backup itself will use a tool that is called <a href="https://github.com/borgbackup/borg#what-is-borgbackup">BorgBackup<a/> which is a well-known server backup tool that efficiently backs up your files and encrypts them on the fly. <br /><br/>
|
||||
Backups get created in the following directory on the host: {{ borg_backup_host_location }}/borg <br /><br/>
|
||||
Be aware that this solution does not back up files and folders that are mounted into Nextcloud using the external storage app. <br /><br/>
|
||||
The backup itself will use a tool that is called <a href="https://github.com/borgbackup/borg#what-is-borgbackup"><b>BorgBackup</b><a/> which is a well-known server backup tool that efficiently backs up your files and encrypts them on the fly. <br /><br/>
|
||||
Backups get created in the following directory on the host: <b>{{ borg_backup_host_location }}/borg</b> <br /><br/>
|
||||
Be aware that this solution does not back up files and folders that are mounted into Nextcloud using the external storage app.
|
||||
|
||||
{% if isApacheStarting != true %}
|
||||
<h3>Backup creation</h3>
|
||||
Clicking on the button below will create a backup.<br><br/>
|
||||
<form method="POST" action="/api/docker/backup" class="xhr">
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
@@ -240,6 +326,7 @@
|
||||
</form>
|
||||
|
||||
{% if has_backup_run_once == true %}
|
||||
<h3>Backup check</h3>
|
||||
Click on the button below to perform a backup integrity check. This is an option that verifies that your backup is intact but it should't be needed in most situtations.<br><br/>
|
||||
<form method="POST" action="/api/docker/backup-check" class="xhr">
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
@@ -247,49 +334,109 @@
|
||||
<input class="button" type="submit" value="Check backup integrity" onclick="return confirm('Check backup integrity? Are you sure that you want to check the backup? This can take a long time depending on the size of your backup.')" /><br/>
|
||||
</form>
|
||||
|
||||
<h3>Backup restore</h3>
|
||||
Choose the backup that you want to restore and click on the button below to restore the selected backup. This will overwrite all your files with the state of the backup so you should consider creating a backup first. It also makes sense to run an integrity check before restoring your files but is not mandatory since it shouldn't be needed in most situations.<br><br>
|
||||
<form method="POST" action="/api/docker/restore" class="xhr" id="restore_selection">
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<select id="selected_restore_time" name="selected_restore_time" form="restore_selection">
|
||||
{% for restore_time in backup_times %}
|
||||
<option value="{{ restore_time }}">{{ restore_time }}</option>
|
||||
<option value="{{ restore_time }}">{{ restore_time }} UTC</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input class="button" type="submit" value="Restore selected backup" onclick="return confirm('Restore the selected backup? Are you sure that you want to restore the selected backup? This will stop all running containers and restore the selected backup. It is recommended to create a backup first. You might also want to check the backup integrity.')" />
|
||||
</form>
|
||||
|
||||
<h3>Daily backup creation</h3>
|
||||
{% if daily_backup_time == "" %}
|
||||
By entering a time below, you can enable daily backups. It will create them at the entered time in 24h format. E.g. <b>04:00</b> will create backups at 4 am UTC and <b>16:00</b> at 4 pm UTC.<br><br/>
|
||||
<form method="POST" action="/api/configuration" class="xhr">
|
||||
<input type="text" name="daily_backup_time" value="04:00" placeholder="04:00"/>
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<input class="button" type="submit" value="Submit" />
|
||||
</form>
|
||||
This option will also automatically update your containers and apps and will send a notification about the result of the backup.<br><br/>
|
||||
{% else %}
|
||||
Daily backups will be created at <b>{{ daily_backup_time }} UTC</b> which includes a notification about the result of the backup and automatic updates of your containers and apps. You can disable this option again by clicking on the button below.<br><br/>
|
||||
<form method="POST" action="/api/configuration" class="xhr">
|
||||
<input type="hidden" name="delete_daily_backup_time" value="yes"/>
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<input class="button" type="submit" value="Disable daily backups" />
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if has_backup_run_once == false %}
|
||||
<br /><br />
|
||||
{% else %}
|
||||
</details><br /><br />
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
<span class="status running"></span> Backup container currently running. (<a href="/api/docker/logs?id=nextcloud-aio-borgbackup">Logs</a>)<br /><br />
|
||||
<a href="" class="button reload">Reload ↻</a><br/>
|
||||
{% if is_backup_container_running == false %}
|
||||
{% if isApacheStarting == false %}
|
||||
<h2>AIO password change</h2>
|
||||
You can change your AIO password below:<br><br />
|
||||
<form method="POST" action="/api/configuration" class="xhr">
|
||||
<input type="text" autocomplete="current-password" name="current-master-password" placeholder="Your current AIO password"/>
|
||||
<input type="text" autocomplete="new-password" name="new-master-password" placeholder="Your new AIO password"/>
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<input class="button" type="submit" value="Submit" />
|
||||
</form>
|
||||
The new password needs to be at least 24 characters long. Allowed characters are the <a href="https://en.wikipedia.org/wiki/Latin_alphabet#/media/File:Abecedarium.png"><b>latin characters</b></a> <b>a-z</b>, <b>A-Z</b>, <b>0-9</b> and <b>spaces</b>.
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<h2>Optional</h2>
|
||||
In this section, you will find optional addons in the future.
|
||||
It will disable the ability to change them when any containers are running and allow to change them when they are stopped.
|
||||
Also, it will display possible sections for optional addons. (which itself will be displayed when enabled and running).
|
||||
{% if is_backup_container_running == false %}
|
||||
<h2>Optional addons</h2>
|
||||
In this section you can find optional addons.<br>
|
||||
You can enable or disable them when your containers are stopped.<br><br>
|
||||
<form id="options-form" method="POST" action="/api/configuration" class="xhr">
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<input type="hidden" name="options-form" value="options-form">
|
||||
{% if is_clamav_enabled == true %}
|
||||
<input type="checkbox" id="clamav" name="clamav" checked="checked"><label for="clamav">ClamAV (only supported on x64, needs ~1GB additional RAM)</label><br>
|
||||
{% else %}
|
||||
<input type="checkbox" id="clamav" name="clamav"><label for="clamav">ClamAV (only supported on x64, needs ~1GB additional RAM)</label><br>
|
||||
{% endif %}
|
||||
{% if is_collabora_enabled == true %}
|
||||
<input type="checkbox" id="collabora" name="collabora" checked="checked"><label for="collabora">Collabora</label><br>
|
||||
{% else %}
|
||||
<input type="checkbox" id="collabora" name="collabora"><label for="collabora">Collabora</label><br>
|
||||
{% endif %}
|
||||
{% if is_talk_enabled == true %}
|
||||
<input type="checkbox" id="talk" name="talk" checked="checked"><label for="talk">Nextcloud Talk (needs ports 3478/TCP and 3478/UDP open in your firewall/router)</label><br><br>
|
||||
{% else %}
|
||||
<input type="checkbox" id="talk" name="talk"><label for="talk">Nextcloud Talk (needs ports 3478/TCP and 3478/UDP open in your firewall/router)</label><br><br>
|
||||
{% endif %}
|
||||
{% if is_onlyoffice_enabled == true %}
|
||||
<input type="checkbox" id="onlyoffice" name="onlyoffice" checked="checked"><label for="onlyoffice">OnlyOffice (only supported on x64)</label><br>
|
||||
{% else %}
|
||||
<input type="checkbox" id="onlyoffice" name="onlyoffice"><label for="onlyoffice">OnlyOffice (only supported on x64)</label><br>
|
||||
{% endif %}
|
||||
<input id="options-form-submit" class="button" type="submit" value="Save changes" />
|
||||
</form>
|
||||
{% if isAnyRunning == true or is_x64_platform == false %}
|
||||
<script type="text/javascript" src="disable-clamav.js"></script>
|
||||
<script type="text/javascript" src="disable-onlyoffice.js"></script>
|
||||
{% endif %}
|
||||
{% if isAnyRunning == true %}
|
||||
<script type="text/javascript" src="disable-talk.js"></script>
|
||||
<script type="text/javascript" src="disable-collabora.js"></script>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if isApacheStarting == true or isBackupContainerRunning == true or isWatchtowerRunning == true %}
|
||||
<script>
|
||||
if (document.hasFocus()) {
|
||||
// hide reload button if the site reloads automatically
|
||||
var list = document.getElementsByClassName("reload button");
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
// list[i] is a node with the desired class name
|
||||
list[i].style.display = 'none';
|
||||
}
|
||||
|
||||
// set timeout for reload
|
||||
setTimeout(function(){
|
||||
window.location.reload(1);
|
||||
}, 5000);
|
||||
}
|
||||
</script>
|
||||
{% if isApacheStarting == true or is_backup_container_running == true or isWatchtowerRunning == true or is_daily_backup_running == true %}
|
||||
<script type="text/javascript" src="automatic_reload.js"></script>
|
||||
{% else %}
|
||||
<script type="text/javascript" src="before-unload.js"></script>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
<link rel="stylesheet" href="/style.css" media="all" />
|
||||
<link rel="icon" href="/img/favicon.png">
|
||||
<script type="text/javascript" src="forms.js"></script>
|
||||
<script type="text/javascript" src="options-form-submit.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
{% extends "layout.twig" %}
|
||||
|
||||
{% block body %}
|
||||
<div class="login-wrapper">
|
||||
<div class="login">
|
||||
<img src="/img/logo-blue.svg" style="margin-left: auto;margin-right: auto;display: block;">
|
||||
<h1>Nextcloud AIO Login</h1>
|
||||
{% if is_login_allowed == true %}
|
||||
<p>Log in using your Nextcloud AIO password.</p>
|
||||
<form method="POST" action="/api/auth/login">
|
||||
<input type="text" name="password" placeholder="Password" />
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<input type="submit" class="button" value="Login" />
|
||||
</form>
|
||||
{% else %}
|
||||
<p>The login is blocked since Nextcloud is running. Please use the automatic login from your Nextcloud.<br><br>
|
||||
You can unblock the login by running 'sudo docker stop nextcloud-aio-apache'.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% extends "layout.twig" %}
|
||||
|
||||
{% block body %}
|
||||
<div class="login-wrapper">
|
||||
<div class="login">
|
||||
<img src="/img/logo-blue.svg" style="margin-left: auto;margin-right: auto;display: block;">
|
||||
<h1>Nextcloud AIO Login</h1>
|
||||
{% if is_login_allowed == true %}
|
||||
<p>Log in using your Nextcloud AIO password:</p>
|
||||
<form method="POST" action="/api/auth/login">
|
||||
<input type="text" name="password" placeholder="Password" />
|
||||
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
|
||||
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
|
||||
<input type="submit" class="button" value="Login" />
|
||||
</form>
|
||||
{% else %}
|
||||
<p>The login is blocked since Nextcloud is running. Please use the automatic login from your Nextcloud.<br><br>
|
||||
You can unblock the login by running 'sudo docker stop nextcloud-aio-apache'.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
<div class="login-wrapper">
|
||||
<div class="login">
|
||||
<img src="/img/logo-blue.svg" style="margin-left: auto;margin-right: auto;display: block;">
|
||||
<h1>Your password for Nextcloud AIO Beta</h1>
|
||||
<p>Please note down the password to access the AIO interface and don't loose it!</p>
|
||||
<h1>Nextcloud AIO setup</h1>
|
||||
<p>Nextcloud AIO stands for Nextcloud All In One and provides easy deployment and maintenance with most features included in this one Nextcloud instance.</p>
|
||||
<p>Please note down the password to access the AIO interface and don't loose it!</p>
|
||||
<strong>Password</strong><br/> <span class="monospace">{{ password }}</span><br>
|
||||
<a href="/" class="button" target="_blank" rel="noopener">Open Nextcloud AIO login ↗</a>
|
||||
</div>
|
||||
|
||||
103
readme.md
103
readme.md
@@ -1,8 +1,4 @@
|
||||
# Nextcloud All In One Beta
|
||||
This is beta software and not production ready.
|
||||
But feel free to use it at your own risk!
|
||||
We expect there to be rough edges and potentially serious bugs.
|
||||
|
||||
# Nextcloud All In One
|
||||
Nextcloud AIO stands for Nextcloud All In One and provides easy deployment and maintenance with most features included in this one Nextcloud instance.
|
||||
|
||||
Included are:
|
||||
@@ -11,30 +7,18 @@ Included are:
|
||||
- High performance backend for Nextcloud Files
|
||||
- High performance backend for Nextcloud Talk
|
||||
- Backup solution (based on [BorgBackup](https://github.com/borgbackup/borg#what-is-borgbackup))
|
||||
|
||||
**Found a bug?** Please file an issue at https://github.com/nextcloud/all-in-one
|
||||
- OnlyOffice
|
||||
- ClamAV
|
||||
|
||||
## How to use this?
|
||||
The following instructions are especially meant for Linux. For macOS see [this](#how-to-run-it-on-macos), for Windows see [this](#how-to-run-it-on-windows).
|
||||
1. Install Docker on your Linux installation using:
|
||||
```
|
||||
curl -fsSL get.docker.com | sudo sh
|
||||
```
|
||||
2. Make sure to pull the latest image:
|
||||
```
|
||||
# For x64 CPUs:
|
||||
sudo docker pull nextcloud/all-in-one:latest
|
||||
```
|
||||
<details>
|
||||
<summary>Command for arm64 CPUs like the Raspberry Pi 4</summary>
|
||||
|
||||
```
|
||||
# For arm64 CPUs:
|
||||
sudo docker pull nextcloud/all-in-one:latest-arm64
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
3. Run the following command in order to start the container:
|
||||
2. Run the following command in order to start the container:<br>
|
||||
(For people that cannot use ports 80 and/or 443 on this server, please follow [this documentation](https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md). Otherwise please run the command below!)
|
||||
```
|
||||
# For x64 CPUs:
|
||||
sudo docker run -it \
|
||||
@@ -65,11 +49,11 @@ Included are:
|
||||
|
||||
</details>
|
||||
|
||||
4. After the initial startup, you should be able to open the Nextcloud AIO Interface now on port 8080 of this server.<br>
|
||||
3. After the initial startup, you should be able to open the Nextcloud AIO Interface now on port 8080 of this server.<br>
|
||||
E.g. `https://internal.ip.of.this.server:8080`<br>
|
||||
If your server has port 80 and 8443 open and you point a domain to your server, you can get a valid certificate automatially by opening the Nextcloud AIO Interface via:<br>
|
||||
If your firewall/router has port 80 and 8443 open and you point a domain to your server, you can get a valid certificate automatially by opening the Nextcloud AIO Interface via:<br>
|
||||
`https://your-domain-that-points-to-this-server.tld:8443`
|
||||
5. Please do not forget to open port `3478/TCP` and `3478/UDP` for the Talk container!
|
||||
4. Please do not forget to open port `3478/TCP` and `3478/UDP` in your firewall/router for the Talk container!
|
||||
|
||||
## FAQ
|
||||
### How does it work?
|
||||
@@ -78,7 +62,7 @@ Nextcloud AIO is inspired by projects like Portainer that allow to manage the do
|
||||
### Are reverse proxies supported?
|
||||
Yes. Please refer to the following documentation on this: [reverse-proxy.md](https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md)
|
||||
|
||||
### Which ports are mandatory to be open?
|
||||
### Which ports are mandatory to be open in your firewall/router?
|
||||
Only those (if you acces the Mastercontainer Interface internally via port 8080):
|
||||
- `443/TCP` for the Apache container
|
||||
- `3478/TCP` and `3478/UDP` for the Talk container
|
||||
@@ -86,9 +70,31 @@ Only those (if you acces the Mastercontainer Interface internally via port 8080)
|
||||
### Explanation of used ports:
|
||||
- `8080/TCP`: Mastercontainer Interface with self-signed certificate (works always, also if only access via IP-address is possible, e.g. `https://internal.ip.address:8080/`)
|
||||
- `80/TCP`: redirects to Nextcloud (is used for getting the certificate via ACME http-challenge for the Mastercontainer)
|
||||
- `8443/TCP`: Mastercontainer Interface with valid certificate (only works if port 80 and 8443 are open and you point a domain to your server. It generates a valid certificate then automatically and access via e.g. `https://public.domain.com:8443/` is possible.)
|
||||
- `443/TCP`: will be used by the Apache container later on and needs to be open
|
||||
- `3478/TCP` and `3478/UDP`: will be used by the Turnserver inside the Talk container and needs to be open
|
||||
- `8443/TCP`: Mastercontainer Interface with valid certificate (only works if port 80 and 8443 are open in your firewall/router and you point a domain to your server. It generates a valid certificate then automatically and access via e.g. `https://public.domain.com:8443/` is possible.)
|
||||
- `443/TCP`: will be used by the Apache container later on and needs to be open in your firewall/router
|
||||
- `3478/TCP` and `3478/UDP`: will be used by the Turnserver inside the Talk container and needs to be open in your firewall/router
|
||||
|
||||
### How to run it on macOS?
|
||||
On macOS, there is one specialty in comparison to Linux: instead of using `--volume /var/run/docker.sock:/var/run/docker.sock:ro`, you need to use `--volume /var/run/docker.sock.raw:/var/run/docker.sock:ro` to run it after you installed [Docker Desktop](https://www.docker.com/products/docker-desktop/). Apart from that it should work and behave the same like on Linux.
|
||||
|
||||
### How to run it on Windows?
|
||||
On Windows, the following command should work after you installed [Docker Desktop](https://www.docker.com/products/docker-desktop/):
|
||||
<details>
|
||||
<summary>Click here to show it</summary>
|
||||
|
||||
```
|
||||
docker run -it ^
|
||||
--name nextcloud-aio-mastercontainer ^
|
||||
--restart always ^
|
||||
-p 80:80 ^
|
||||
-p 8080:8080 ^
|
||||
-p 8443:8443 ^
|
||||
--volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config ^
|
||||
--volume //var/run/docker.sock:/var/run/docker.sock:ro ^
|
||||
nextcloud/all-in-one:latest
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### How to run `occ` commands?
|
||||
Simply run the following: `sudo docker exec -it nextcloud-aio-nextcloud php occ your-command`. Of course `your-command` needs to be exchanged with the command that you want to run.
|
||||
@@ -96,6 +102,9 @@ Simply run the following: `sudo docker exec -it nextcloud-aio-nextcloud php occ
|
||||
### How to resolve `Security & setup warnings displays the "missing default phone region" after initial install`?
|
||||
Simply run the following command: `sudo docker exec -it nextcloud-aio-nextcloud php occ config:system:set default_phone_region --value="yourvalue"`. Of course you need to modify `yourvalue` based on your location. Examples are `DE`, `EN` and `GB`. See this list for more codes: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements
|
||||
|
||||
### How to switch the channel?
|
||||
You can switch to a different channel like e.g. the beta channel or from the beta channel back to the latest channel by stopping the mastercontainer, removing it (no data will be lost) and recreating the container using the same command that you used initially to create the mastercontainer. For the beta channel on x64 you need to change the last line `nextcloud/all-in-one:latest` to `nextcloud/all-in-one:beta` and vice versa. For arm64 it is `nextcloud/all-in-one:latest-arm64` and `nextcloud/all-in-one:beta-arm64`, respectively.
|
||||
|
||||
### How to update the containers?
|
||||
If we push new containers to `latest`, you will see in the AIO interface below the `containers` section that new container updates were found. In this case, just press `Stop containers` and `Start containers` in order to update the containers. The mastercontainer has its own update procedure though. See below. And don't forget to back up the current state of your instance using the built-in backup solution before starting the containers again! Otherwise you won't be able to restore your instance easily if something should break during the update.
|
||||
|
||||
@@ -104,15 +113,28 @@ If a new `Mastercontainer` update was found, you'll see an additional section be
|
||||
Additionally, there is a cronjob that runs once a day that checks for container and mastercontainer updates and sends a notification to all Nextcloud admins if a new update was found.
|
||||
|
||||
### How to easily log in to the AIO interface?
|
||||
If your Nextcloud is running and you are logged in as admin in your Nextcloud, you can easily log in to the AIO interface by opening `https://yourdomain.tld/settings/admin/overview` which will show a button on top that enables you to log in to the AIO interface by just clicking on this button.
|
||||
If your Nextcloud is running and you are logged in as admin in your Nextcloud, you can easily log in to the AIO interface by opening `https://yourdomain.tld/settings/admin/overview` which will show a button on top that enables you to log in to the AIO interface by just clicking on this button. **Note:** You can change the domain/ip-address/port of the button by simply stopping the containers, visiting the AIO interface from the correct and desired domain/ip-address/port and clicking once on `Start containers`.
|
||||
|
||||
### Backup solution
|
||||
Nextcloud AIO provides a local backup solution based on BorgBackup. These backups act as a local restore point in case the installation gets corrupted.
|
||||
Nextcloud AIO provides a local backup solution based on [BorgBackup](https://github.com/borgbackup/borg#what-is-borgbackup). These backups act as a local restore point in case the installation gets corrupted.
|
||||
|
||||
It is recommended to create a backup before any container update. By doing this, you will be safe regarding any possible complication during updates because you will be able to restore the whole instance with basically one click.
|
||||
|
||||
If you connect an external drive to your host, and choose the backup directory to be on that drive, you are also kind of save against drive failures of the drive where the docker volumes are stored on.
|
||||
|
||||
<details>
|
||||
<summary>How to do the above step for step</summary>
|
||||
|
||||
<br>
|
||||
|
||||
1. Mount an external/backup HDD to the host OS using the built-in functionality or udev rules or whatever way you prefer. (E.g. follow this video: https://www.youtube.com/watch?v=2lSyX4D3v_s) and mount the drive in best case in `/mnt/backup`.
|
||||
2. If not already done, fire up the docker container and set up Nextcloud as per the guide.
|
||||
3. Now open the AIO interface.
|
||||
4. Under backup section, add your external disk mountpoint as backup directory, e.g. `/mnt/backup`.
|
||||
5. Click on `Create Backup` which should create the first backup on the external disk.
|
||||
|
||||
</details>
|
||||
|
||||
Backups can be created and restored in the AIO interface using the buttons `Create Backup` and `Restore selected backup`. Additionally, a backup check is provided that checks the integrity of your backups but it shouldn't be needed in most situations.
|
||||
|
||||
The backups itself get encrypted with an encryption key that gets shown to you in the AIO interface. Please save that at a safe place as you will not be able to restore from backup without this key.
|
||||
@@ -200,6 +222,11 @@ if ! [ -d "$SOURCE_DIRECTORY" ]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$(ls -A "$SOURCE_DIRECTORY/")" ]; then
|
||||
echo "The source directory is empty which is not allowed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [ -d "$DRIVE_MOUNTPOINT" ]; then
|
||||
echo "The drive mountpoint must be an existing directory"
|
||||
exit 1
|
||||
@@ -235,13 +262,13 @@ if ! rsync --stats --archive --human-readable --delete "$SOURCE_DIRECTORY/" "$TA
|
||||
fi
|
||||
|
||||
umount "$DRIVE_MOUNTPOINT"
|
||||
if mountpoint -q "$DRIVE_MOUNTPOINT"; then
|
||||
echo "Synced the backup repository successfully but failed to unmount the target drive."
|
||||
exit 0
|
||||
|
||||
if docker ps --format "{{.Names}}" | grep "^nextcloud-aio-nextcloud$"; then
|
||||
docker exec -it nextcloud-aio-nextcloud bash /notify.sh "Rsync backup successful!" "Synced the backup repository successfully."
|
||||
else
|
||||
echo "Synced the backup repository successfully."
|
||||
fi
|
||||
|
||||
echo "Synced the backup repository successfully and unmounted the target drive."
|
||||
exit 0
|
||||
```
|
||||
|
||||
</details>
|
||||
@@ -251,10 +278,10 @@ You can simply copy and past the script into a file e.g. named `backup-script.sh
|
||||
Afterwards apply the correct permissions with `sudo chown root:root /root/backup-script.sh` and `sudo chmod 700 /root/backup-script.sh`. Then you can create a cronjob that runs e.g. at `20:00` each week on sundays like this: `crontab -u root -l | { cat; echo "0 20 * * 7 /root/backup-script.sh"; } | crontab -u root -`. Make sure that it does not collidate with the daily backups from AIO (if configured) since the target backup repository might get into an inconsistent state. (There is no check in place that checks this.)
|
||||
|
||||
### How to change the default location of Nextcloud's Datadir?
|
||||
You can configure the Nextcloud container to use a specific directory on your host as data directory. You can do so by adding the environmental variable `NEXTCLOUD_DATADIR` to the initial startup of the mastercontainer. Allowed values for that variable are strings that start with `/mnt/` or `/media/`. An example for this is `-e NEXTCLOUD_DATADIR="/mnt/ncdata"`. Please make sure to apply the correct permissions to the chosen directory before starting Nextcloud the first time. In this example would the command for this be: `sudo chown -R 33:0 /mnt/ncdata`. **Attention:** It is very important to change the datadir **before** Nextcloud is installed/started the first time and not to change it afterwards!
|
||||
You can configure the Nextcloud container to use a specific directory on your host as data directory. You can do so by adding the environmental variable `NEXTCLOUD_DATADIR` to the initial startup of the mastercontainer. Allowed values for that variable are strings that start with `/mnt/`, `/media/` or `/host_mnt/`. An example for Linux and macOS is `-e NEXTCLOUD_DATADIR="/mnt/ncdata"`. On Windows it might be `-e NEXTCLOUD_DATADIR="/host_mnt/c/your/data/path"` (This Windows example would be equivalent to `C:\your\data\path` on the Windows host. So you need to translate the path that you want to use into the correct format.) Please make sure to apply the correct permissions to the chosen directory before starting Nextcloud the first time (not needed on Windows). In this example would the command for this be: `sudo chown -R 33:0 /mnt/ncdata`. ⚠ **Attention:** It is very important to change the datadir **before** Nextcloud is installed/started the first time and not to change it afterwards!
|
||||
|
||||
### How to allow the Nextcloud container to access directories on the host?
|
||||
By default, the Nextcloud container is confined and cannot access directories on the host OS. You might want to change this when you are planning to use local external storage in Nextcloud to store some files outside the data directory and can do so by adding the environmental variable `NEXTCLOUD_MOUNT` to the initial startup of the mastercontainer. Allowed values for that variable are strings that are equal to or start with `/mnt/` or `/media/` or are equal to `/var/backups` and unequal to `/mnt/ncdata`. Two examples for this are: `-e NEXTCLOUD_MOUNT="/mnt/"` or `-e NEXTCLOUD_MOUNT="/media/"`. After doing so, please make sure to apply the correct permissions to the directories that you want to use in Nextcloud. E.g. `sudo chown -R 33:0 /mnt/your-drive-mountpoint` should make it work. You can then navigate to the apps management page, activate the external storage app, navigate to `https://your-nc-domain.com/settings/admin/externalstorages` and add a local external storage directory that will be accessible inside the container at the same place that you've entered. E.g. `/mnt/your-drive-mountpoint` will be mounted to `/mnt/your-drive-mountpoint` inside the container, etc. Be aware though that these locations will not be covered by the built-in backup solution!
|
||||
By default, the Nextcloud container is confined and cannot access directories on the host OS. You might want to change this when you are planning to use local external storage in Nextcloud to store some files outside the data directory and can do so by adding the environmental variable `NEXTCLOUD_MOUNT` to the initial startup of the mastercontainer. Allowed values for that variable are strings that are equal to or start with `/mnt/`, `/media/` or `/host_mnt/` or are equal to `/var/backups` and unequal to `/mnt/ncdata`. Two examples for Linux and macOS are: `-e NEXTCLOUD_MOUNT="/mnt/"` or `-e NEXTCLOUD_MOUNT="/media/"`. On Windows it might be `-e NEXTCLOUD_DATADIR="/host_mnt/c"` (This Windows example would be equivalent to `C:\` on the Windows host. So you need to translate the path that you want to use into the correct format.) After using this option, please make sure to apply the correct permissions to the directories that you want to use in Nextcloud (not needed on Windows). E.g. `sudo chown -R 33:0 /mnt/your-drive-mountpoint` should make it work. You can then navigate to the apps management page, activate the external storage app, navigate to `https://your-nc-domain.com/settings/admin/externalstorages` and add a local external storage directory that will be accessible inside the container at the same place that you've entered. E.g. `/mnt/your-drive-mountpoint` will be mounted to `/mnt/your-drive-mountpoint` inside the container, etc. Be aware though that these locations will not be covered by the built-in backup solution!
|
||||
|
||||
### Huge docker logs
|
||||
When your containers run for a few days without a restart, the container logs that you can view from the AIO interface can get really huge. You can limit the loge sizes by enabling logrotate for docker container logs. Feel free to enable this by following those instructions: https://sandro-keil.de/blog/logrotate-for-docker-container/
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
## Reverse Proxy Config
|
||||
## Reverse Proxy Documentation
|
||||
|
||||
Basically, you need to specify the port that the apache container shall use and modify the startup command a bit.
|
||||
|
||||
All examples below will use port `11000` as example apache port. Also it is supposed that the reverse proxy runs on the same server like AIO, hence `localhost` is used and not an internal ip-address to point to the AIO instance. Modify both to your needings.
|
||||
|
||||
### Caddy reverse proxy config example
|
||||
**Info:** The instructions below assume that your reverse proxy is installed directly on the host, not inside a separate docker container. If you want to run the reverse proxy inside a docker container, you can do so by using the `--network host` option when starting the reverse proxy container.
|
||||
|
||||
### Reverse proxy config examples
|
||||
|
||||
#### Caddy
|
||||
|
||||
<details>
|
||||
|
||||
<summary>click here to expand</summary>
|
||||
<br>
|
||||
Add this to your Caddyfile:
|
||||
|
||||
```
|
||||
@@ -17,8 +25,41 @@ https://<your-nc-domain>:443 {
|
||||
|
||||
Of course you need to modify `<your-nc-domain>` to the domain on which you want to run Nextcloud.
|
||||
|
||||
</details>
|
||||
|
||||
#### Nginx
|
||||
|
||||
<details>
|
||||
|
||||
<summary>click here to expand</summary>
|
||||
<br>
|
||||
|
||||
**Disclaimer:** the config below is not working 100% correctly, yet. See e.g. https://github.com/nextcloud/all-in-one/issues/450, https://github.com/nextcloud/all-in-one/issues/447 and https://github.com/nextcloud/all-in-one/issues/491. Improvements to it are very welcome!
|
||||
|
||||
Add this to you nginx config:
|
||||
|
||||
```
|
||||
location / {
|
||||
proxy_pass http://localhost:11000;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
# Websocket
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection $connection_upgrade;
|
||||
}
|
||||
```
|
||||
|
||||
Of course SSL needs to be set up as well e.g. by using certbot and your domain must be also added inside the nginx config.
|
||||
|
||||
</details>
|
||||
|
||||
### Startup command
|
||||
|
||||
After adjusting your reverse proxy config, use the following command to start AIO:
|
||||
|
||||
```
|
||||
# For x64 CPUs:
|
||||
sudo docker run -it \
|
||||
@@ -49,7 +90,26 @@ nextcloud/all-in-one:latest-arm64
|
||||
|
||||
</details>
|
||||
|
||||
After doing so, you should be able to access the AIO Interface via `https://internal.ip.of.this.server:8080`. Enter your domain that you've entered in the reverse proxy config and you should be done. Please do not forget to open port `3478/TCP` and `3478/UDP` for the Talk container!
|
||||
On macOS see https://github.com/nextcloud/all-in-one#how-to-run-it-on-macos.
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Command for Windows</summary>
|
||||
|
||||
```
|
||||
docker run -it ^
|
||||
--name nextcloud-aio-mastercontainer ^
|
||||
--restart always ^
|
||||
-p 8080:8080 ^
|
||||
-e APACHE_PORT=11000 ^
|
||||
--volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config ^
|
||||
--volume //var/run/docker.sock:/var/run/docker.sock:ro ^
|
||||
nextcloud/all-in-one:latest
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
After doing so, you should be able to access the AIO Interface via `https://internal.ip.of.this.server:8080`. Enter your domain that you've entered in the reverse proxy config and you should be done. Please do not forget to open port `3478/TCP` and `3478/UDP` in your firewall/router for the Talk container!
|
||||
|
||||
### Optional
|
||||
|
||||
@@ -65,4 +125,4 @@ https://<your-nc-domain>:8443 {
|
||||
}
|
||||
```
|
||||
|
||||
Of course you also need to modify `<your-nc-domain>` to the domain that you want to use. Afterwards should the AIO interface be accessible via `https://<your-nc-domain>:8443`.
|
||||
Of course, you also need to modify `<your-nc-domain>` to the domain that you want to use. Afterwards should the AIO interface be accessible via `https://<your-nc-domain>:8443`. You can alternatively change the domain to a different subdomain by using `https://<your-alternative-domain>:443` in the Caddyfile and use that to access the AIO interface.
|
||||
|
||||
Reference in New Issue
Block a user