mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
Merge pull request #1201 from bunkerity/dev
Merge branch "dev" into branch "staging"
This commit is contained in:
commit
9b38952a3c
177 changed files with 2300 additions and 1632 deletions
2
.github/workflows/beta.yml
vendored
2
.github/workflows/beta.yml
vendored
|
|
@ -135,7 +135,7 @@ jobs:
|
|||
versionrpm: ${{ steps.getversionrpm.outputs.versionrpm }}
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Get VERSION
|
||||
id: getversion
|
||||
run: echo "version=$(cat src/VERSION | tr -d '\n')" >> "$GITHUB_OUTPUT"
|
||||
|
|
|
|||
6
.github/workflows/codeql.yml
vendored
6
.github/workflows/codeql.yml
vendored
|
|
@ -19,7 +19,7 @@ jobs:
|
|||
language: ["python", "javascript"]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
|
||||
if: matrix.language == 'python'
|
||||
|
|
@ -35,12 +35,12 @@ jobs:
|
|||
python -m pip install --no-cache-dir --require-hashes -r src/common/db/requirements.txt
|
||||
echo "CODEQL_PYTHON=$(which python)" >> $GITHUB_ENV
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@ccf74c947955fd1cf117aef6a0e4e66191ef6f61 # v3.25.4
|
||||
uses: github/codeql-action/init@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
config-file: ./.github/codeql.yml
|
||||
setup-python-dependencies: false
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@ccf74c947955fd1cf117aef6a0e4e66191ef6f61 # v3.25.4
|
||||
uses: github/codeql-action/analyze@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
|
|
|
|||
2
.github/workflows/container-build.yml
vendored
2
.github/workflows/container-build.yml
vendored
|
|
@ -45,7 +45,7 @@ jobs:
|
|||
steps:
|
||||
# Prepare
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Replace VERSION
|
||||
if: inputs.RELEASE == 'testing' || inputs.RELEASE == 'dev'
|
||||
run: ./misc/update-version.sh ${{ inputs.RELEASE }}
|
||||
|
|
|
|||
2
.github/workflows/create-arm.yml
vendored
2
.github/workflows/create-arm.yml
vendored
|
|
@ -33,7 +33,7 @@ jobs:
|
|||
steps:
|
||||
# Prepare
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Get ARM availabilities
|
||||
id: availabilities
|
||||
uses: scaleway/action-scw@be2696f261325a78354eda14988c80405f33e082
|
||||
|
|
|
|||
2
.github/workflows/dev-update-mmdb.yml
vendored
2
.github/workflows/dev-update-mmdb.yml
vendored
|
|
@ -12,7 +12,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.BUNKERBOT_TOKEN }}
|
||||
|
|
|
|||
4
.github/workflows/dev.yml
vendored
4
.github/workflows/dev.yml
vendored
|
|
@ -80,7 +80,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- id: set-matrix
|
||||
run: |
|
||||
tests=$(find ./tests/ui/ -name "*_page.py" -type f -printf "%f\n" | jq -c --raw-input --slurp 'split("\n")| .[0:-1]')
|
||||
|
|
@ -113,7 +113,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- id: set-matrix
|
||||
run: |
|
||||
tests=$(find ./tests/core/ -maxdepth 1 -mindepth 1 -type d -printf "%f\n" | jq -c --raw-input --slurp 'split("\n")| .[0:-1]')
|
||||
|
|
|
|||
2
.github/workflows/doc-to-pdf.yml
vendored
2
.github/workflows/doc-to-pdf.yml
vendored
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
steps:
|
||||
# Prepare
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Install Python
|
||||
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
|
||||
with:
|
||||
|
|
|
|||
2
.github/workflows/linux-build.yml
vendored
2
.github/workflows/linux-build.yml
vendored
|
|
@ -37,7 +37,7 @@ jobs:
|
|||
steps:
|
||||
# Prepare
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Replace VERSION
|
||||
if: inputs.RELEASE == 'testing' || inputs.RELEASE == 'dev' || inputs.RELEASE == 'ui'
|
||||
run: ./misc/update-version.sh ${{ inputs.RELEASE }}
|
||||
|
|
|
|||
2
.github/workflows/push-doc.yml
vendored
2
.github/workflows/push-doc.yml
vendored
|
|
@ -18,7 +18,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.BUNKERBOT_TOKEN }}
|
||||
|
|
|
|||
2
.github/workflows/push-docker.yml
vendored
2
.github/workflows/push-docker.yml
vendored
|
|
@ -33,7 +33,7 @@ jobs:
|
|||
steps:
|
||||
# Prepare
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
|
||||
with:
|
||||
|
|
|
|||
2
.github/workflows/push-github.yml
vendored
2
.github/workflows/push-github.yml
vendored
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Checkout
|
||||
- uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
- uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
# Get PDF doc
|
||||
- name: Get documentation
|
||||
if: inputs.VERSION != 'testing'
|
||||
|
|
|
|||
4
.github/workflows/push-packagecloud.yml
vendored
4
.github/workflows/push-packagecloud.yml
vendored
|
|
@ -40,9 +40,9 @@ jobs:
|
|||
steps:
|
||||
# Prepare
|
||||
- name: Check out repository code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Install ruby
|
||||
uses: ruby/setup-ruby@cacc9f1c0b3f4eb8a16a6bb0ed10897b43b9de49 # v1.176.0
|
||||
uses: ruby/setup-ruby@7dc18ff0ca6e3630d3f29d2a85ebf6cc27ae9d6c # v1.177.0
|
||||
with:
|
||||
ruby-version: "3.0"
|
||||
- name: Install packagecloud
|
||||
|
|
|
|||
15
.github/workflows/release.yml
vendored
15
.github/workflows/release.yml
vendored
|
|
@ -7,8 +7,8 @@ on:
|
|||
branches: [master]
|
||||
|
||||
jobs:
|
||||
scorecards-analysis:
|
||||
uses: ./.github/workflows/scorecards-analysis.yml
|
||||
# scorecards-analysis:
|
||||
# uses: ./.github/workflows/scorecards-analysis.yml
|
||||
|
||||
codeql:
|
||||
uses: ./.github/workflows/codeql.yml
|
||||
|
|
@ -106,6 +106,9 @@ jobs:
|
|||
matrix:
|
||||
linux: [ubuntu, ubuntu-noble, debian, fedora, rhel, rhel9]
|
||||
platforms: [linux/amd64, linux/arm64]
|
||||
exclude:
|
||||
- linux: ubuntu-noble
|
||||
platforms: linux/arm64
|
||||
include:
|
||||
- release: latest
|
||||
- linux: ubuntu
|
||||
|
|
@ -143,7 +146,7 @@ jobs:
|
|||
versionrpm: ${{ steps.getversionrpm.outputs.versionrpm }}
|
||||
steps:
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Get VERSION
|
||||
id: getversion
|
||||
run: echo "version=$(cat src/VERSION | tr -d '\n')" >> "$GITHUB_OUTPUT"
|
||||
|
|
@ -195,6 +198,9 @@ jobs:
|
|||
matrix:
|
||||
linux: [ubuntu, ubuntu-noble, debian, fedora, el, el9]
|
||||
arch: [amd64, arm64]
|
||||
exclude:
|
||||
- linux: ubuntu-noble
|
||||
arch: arm64
|
||||
include:
|
||||
- release: latest
|
||||
repo: bunkerweb
|
||||
|
|
@ -249,9 +255,6 @@ jobs:
|
|||
- linux: ubuntu
|
||||
arch: arm64
|
||||
package_arch: arm64
|
||||
- linux: ubuntu-noble
|
||||
arch: arm64
|
||||
package_arch: arm64
|
||||
- linux: debian
|
||||
arch: arm64
|
||||
package_arch: arm64
|
||||
|
|
|
|||
2
.github/workflows/rm-arm.yml
vendored
2
.github/workflows/rm-arm.yml
vendored
|
|
@ -21,7 +21,7 @@ jobs:
|
|||
steps:
|
||||
# Prepare
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Delete ARM VM
|
||||
uses: scaleway/action-scw@be2696f261325a78354eda14988c80405f33e082
|
||||
with:
|
||||
|
|
|
|||
4
.github/workflows/scorecards-analysis.yml
vendored
4
.github/workflows/scorecards-analysis.yml
vendored
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: "Run analysis"
|
||||
|
|
@ -25,6 +25,6 @@ jobs:
|
|||
results_format: sarif
|
||||
publish_results: true
|
||||
- name: "Upload SARIF results to code scanning"
|
||||
uses: github/codeql-action/upload-sarif@ccf74c947955fd1cf117aef6a0e4e66191ef6f61 # v3.25.4
|
||||
uses: github/codeql-action/upload-sarif@9fdb3e49720b44c48891d036bb502feb25684276 # v3.25.6
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
|
|
|||
2
.github/workflows/staging-create-infra.yml
vendored
2
.github/workflows/staging-create-infra.yml
vendored
|
|
@ -21,7 +21,7 @@ jobs:
|
|||
run: ssh-keygen -b 2048 -t rsa -f ~/.ssh/id_rsa -q -N "" && ssh-keygen -f ~/.ssh/id_rsa -y > ~/.ssh/id_rsa.pub && echo -e "Host *\n StrictHostKeyChecking no" > ~/.ssh/ssh_config
|
||||
if: inputs.TYPE != 'k8s'
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Install terraform
|
||||
uses: hashicorp/setup-terraform@651471c36a6092792c552e8b1bef71e592b462d8 # v3.1.1
|
||||
- name: Install kubectl
|
||||
|
|
|
|||
2
.github/workflows/staging-delete-infra.yml
vendored
2
.github/workflows/staging-delete-infra.yml
vendored
|
|
@ -20,7 +20,7 @@ jobs:
|
|||
steps:
|
||||
# Prepare
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Install terraform
|
||||
uses: hashicorp/setup-terraform@651471c36a6092792c552e8b1bef71e592b462d8 # v3.1.1
|
||||
- uses: actions/download-artifact@65a9edc5881444af0b9093a5e628f2fe47ea3b2e # v4.1.7
|
||||
|
|
|
|||
2
.github/workflows/staging-tests.yml
vendored
2
.github/workflows/staging-tests.yml
vendored
|
|
@ -25,7 +25,7 @@ jobs:
|
|||
steps:
|
||||
# Prepare
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Login to ghcr
|
||||
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
|
||||
with:
|
||||
|
|
|
|||
4
.github/workflows/staging.yml
vendored
4
.github/workflows/staging.yml
vendored
|
|
@ -91,7 +91,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- id: set-matrix
|
||||
run: |
|
||||
tests=$(find ./tests/core/ -maxdepth 1 -mindepth 1 -type d -printf "%f\n" | jq -c --raw-input --slurp 'split("\n")| .[0:-1]')
|
||||
|
|
@ -102,7 +102,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- id: set-matrix
|
||||
run: |
|
||||
tests=$(find ./tests/ui/ -name "*_page.py" -type f -printf "%f\n" | jq -c --raw-input --slurp 'split("\n")| .[0:-1]')
|
||||
|
|
|
|||
2
.github/workflows/test-core-linux.yml
vendored
2
.github/workflows/test-core-linux.yml
vendored
|
|
@ -16,7 +16,7 @@ jobs:
|
|||
steps:
|
||||
# Prepare
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
|
||||
with:
|
||||
|
|
|
|||
2
.github/workflows/test-core.yml
vendored
2
.github/workflows/test-core.yml
vendored
|
|
@ -16,7 +16,7 @@ jobs:
|
|||
steps:
|
||||
# Prepare
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Login to ghcr
|
||||
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
|
||||
with:
|
||||
|
|
|
|||
2
.github/workflows/tests-ui-linux.yml
vendored
2
.github/workflows/tests-ui-linux.yml
vendored
|
|
@ -16,7 +16,7 @@ jobs:
|
|||
steps:
|
||||
# Prepare
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Set up Python 3.9
|
||||
uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
|
||||
with:
|
||||
|
|
|
|||
2
.github/workflows/tests-ui.yml
vendored
2
.github/workflows/tests-ui.yml
vendored
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
steps:
|
||||
# Prepare
|
||||
- name: Checkout source code
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- name: Login to ghcr
|
||||
uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
|
||||
with:
|
||||
|
|
|
|||
2
.github/workflows/ui.yml
vendored
2
.github/workflows/ui.yml
vendored
|
|
@ -67,7 +67,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@44c2b7a8a4ea60a981eaca3cf939b5f4305c123b # v4.1.5
|
||||
uses: actions/checkout@a5ac7e51b41094c92402da3b24376905380afc29 # v4.1.6
|
||||
- id: set-matrix
|
||||
run: |
|
||||
tests=$(find ./tests/ui/ -name "*_page.py" -type f -printf "%f\n" | jq -c --raw-input --slurp 'split("\n")| .[0:-1]')
|
||||
|
|
|
|||
|
|
@ -10,3 +10,4 @@ src/ui/templates/settings_plugins.html:hashicorp-tf-password:297
|
|||
src/ui/templates/settings_plugins.html:hashicorp-tf-password:106
|
||||
src/ui/templates/account.html:hashicorp-tf-password:154
|
||||
src/ui/templates/account.html:hashicorp-tf-password:162
|
||||
src/common/core/errors/files/error.html:aws-access-token:20
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ repos:
|
|||
- id: check-case-conflict
|
||||
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 8fe627072f15ff2e3d380887b92f7868efaf6d05 # frozen: 24.4.0
|
||||
rev: 3702ba224ecffbcec30af640c149f231d90aebdb # frozen: 24.4.2
|
||||
hooks:
|
||||
- id: black
|
||||
name: Black Python Formatter
|
||||
|
|
@ -61,7 +61,7 @@ repos:
|
|||
hooks:
|
||||
- id: codespell
|
||||
name: Codespell Spell Checker
|
||||
exclude: (^src/(ui/templates|common/core/.+/files|bw/loading)/.+.html|modsecurity-rules.conf.*)$
|
||||
exclude: (^src/(ui/templates|common/core/.+/files|bw/loading)/.+.html|modsecurity-rules.conf.*|src/ui/static/js/lottie-web.min.js)$
|
||||
entry: codespell --ignore-regex="(tabEl|Widgits)" --skip src/ui/static/js/utils/flatpickr.js,src/ui/static/css/style.css,CHANGELOG.md
|
||||
language: python
|
||||
types: [text]
|
||||
|
|
|
|||
|
|
@ -13,8 +13,10 @@ README.md
|
|||
SECURITY.md
|
||||
tsparticles.bundle.min.js
|
||||
flatpickr.*
|
||||
src/ui/static/js/lottie-web.min.js
|
||||
src/ui/static/js/editor/*
|
||||
src/ui/static/js/utils/purify/*
|
||||
src/ui/static/json/particles.json
|
||||
src/ui/templates/*
|
||||
src/common/core/*/ui/*
|
||||
datepicker-foundation.css
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
# Changelog
|
||||
|
||||
## v1.5.7 - ????/??/??
|
||||
## v1.5.8 - ????/??/??
|
||||
|
||||
- [FEATURE] Add nightly build of the OWASP coreruleset that are automatically downloaded and updated
|
||||
- [FEATURE] Enhance security on error pages, default server page and loading page by adding a custom `Content-Security-Policy` header with nonces and removing the `Server` header
|
||||
|
||||
## v1.5.7 - 2024/05/14
|
||||
|
||||
- [LINUX] Support Ubuntu 24.04 (Noble)
|
||||
- [LINUX] Support RHEL 9.4 instead of 9.3
|
||||
|
|
|
|||
|
|
@ -1204,6 +1204,6 @@ You can easily deploy BunkerWeb on your Azure subscription in several ways:
|
|||
|
||||
Login in [Azure portal](https://portal.azure.com){:target="_blank"}.
|
||||
|
||||
Get BunkerWeb from the [Create ressource menu](https://portal.azure.com/#view/Microsoft_Azure_Marketplace/GalleryItemDetailsBladeNopdl/id/bunkerity.bunkerweb){:target="_blank"}.
|
||||
Get BunkerWeb from the [Create resource menu](https://portal.azure.com/#view/Microsoft_Azure_Marketplace/GalleryItemDetailsBladeNopdl/id/bunkerity.bunkerweb){:target="_blank"}.
|
||||
|
||||
You can also go through the [Marketplace](https://azuremarketplace.microsoft.com/fr-fr/marketplace/apps/bunkerity.bunkerweb?tab=Overview){:target="_blank"}.
|
||||
You can also go through the [Marketplace](https://azuremarketplace.microsoft.com/fr-fr/marketplace/apps/bunkerity.bunkerweb?tab=Overview){:target="_blank"}.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
mike==2.1.1
|
||||
mkdocs-material[imaging]==9.5.22
|
||||
mkdocs-material[imaging]==9.5.24
|
||||
mkdocs-print-site-plugin==2.4.1
|
||||
pytablewriter==1.2.0
|
||||
|
|
|
|||
|
|
@ -317,9 +317,9 @@ mkdocs-get-deps==0.2.0 \
|
|||
--hash=sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c \
|
||||
--hash=sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134
|
||||
# via mkdocs
|
||||
mkdocs-material==9.5.22 \
|
||||
--hash=sha256:22a853a456ae8c581c4628159574d6fc7c71b2c7569dc9c3a82cc70432219599 \
|
||||
--hash=sha256:8c7a377d323567934e6cd46915e64dc209efceaec0dec1cf2202184f5649862c
|
||||
mkdocs-material==9.5.24 \
|
||||
--hash=sha256:02d5aaba0ee755e707c3ef6e748f9acb7b3011187c0ea766db31af8905078a34 \
|
||||
--hash=sha256:e12cd75954c535b61e716f359cf2a5056bf4514889d17161fdebd5df4b0153c6
|
||||
# via
|
||||
# -r requirements.in
|
||||
# mkdocs-print-site-plugin
|
||||
|
|
@ -421,9 +421,9 @@ pillow==10.3.0 \
|
|||
# via
|
||||
# cairosvg
|
||||
# mkdocs-material
|
||||
platformdirs==4.2.1 \
|
||||
--hash=sha256:031cd18d4ec63ec53e82dceaac0417d218a6863f7745dfcc9efe7793b7039bdf \
|
||||
--hash=sha256:17d5a1161b3fd67b390023cb2d3b026bbd40abde6fdb052dfbd3a29c3ba22ee1
|
||||
platformdirs==4.2.2 \
|
||||
--hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \
|
||||
--hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3
|
||||
# via mkdocs-get-deps
|
||||
pycparser==2.22 \
|
||||
--hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \
|
||||
|
|
@ -519,98 +519,98 @@ pyyaml-env-tag==0.1 \
|
|||
# via
|
||||
# mike
|
||||
# mkdocs
|
||||
regex==2024.5.10 \
|
||||
--hash=sha256:031219782d97550c2098d9a68ce9e9eaefe67d2d81d8ff84c8354f9c009e720c \
|
||||
--hash=sha256:0709ba544cf50bd5cb843df4b8bb6701bae2b70a8e88da9add8386cbca5c1385 \
|
||||
--hash=sha256:0a9f89d7db5ef6bdf53e5cc8e6199a493d0f1374b3171796b464a74ebe8e508a \
|
||||
--hash=sha256:0bc94873ba11e34837bffd7e5006703abeffc4514e2f482022f46ce05bd25e67 \
|
||||
--hash=sha256:0ce56a923f4c01d7568811bfdffe156268c0a7aae8a94c902b92fe34c4bde785 \
|
||||
--hash=sha256:0faecb6d5779753a6066a3c7a0471a8d29fe25d9981ca9e552d6d1b8f8b6a594 \
|
||||
--hash=sha256:1118ba9def608250250f4b3e3f48c62f4562ba16ca58ede491b6e7554bfa09ff \
|
||||
--hash=sha256:12446827f43c7881decf2c126762e11425de5eb93b3b0d8b581344c16db7047a \
|
||||
--hash=sha256:14905ed75c7a6edf423eb46c213ed3f4507c38115f1ed3c00f4ec9eafba50e58 \
|
||||
--hash=sha256:15e593386ec6331e0ab4ac0795b7593f02ab2f4b30a698beb89fbdc34f92386a \
|
||||
--hash=sha256:160ba087232c5c6e2a1e7ad08bd3a3f49b58c815be0504d8c8aacfb064491cd8 \
|
||||
--hash=sha256:161a206c8f3511e2f5fafc9142a2cc25d7fe9a1ec5ad9b4ad2496a7c33e1c5d2 \
|
||||
--hash=sha256:169fd0acd7a259f58f417e492e93d0e15fc87592cd1e971c8c533ad5703b5830 \
|
||||
--hash=sha256:193b7c6834a06f722f0ce1ba685efe80881de7c3de31415513862f601097648c \
|
||||
--hash=sha256:1a3903128f9e17a500618e80c68165c78c741ebb17dd1a0b44575f92c3c68b02 \
|
||||
--hash=sha256:1d5bd666466c8f00a06886ce1397ba8b12371c1f1c6d1bef11013e9e0a1464a8 \
|
||||
--hash=sha256:224a9269f133564109ce668213ef3cb32bc72ccf040b0b51c72a50e569e9dc9e \
|
||||
--hash=sha256:236cace6c1903effd647ed46ce6dd5d76d54985fc36dafc5256032886736c85d \
|
||||
--hash=sha256:249fbcee0a277c32a3ce36d8e36d50c27c968fdf969e0fbe342658d4e010fbc8 \
|
||||
--hash=sha256:29d839829209f3c53f004e1de8c3113efce6d98029f044fa5cfee666253ee7e6 \
|
||||
--hash=sha256:2c8982ee19ccecabbaeac1ba687bfef085a6352a8c64f821ce2f43e6d76a9298 \
|
||||
--hash=sha256:2f30a5ab8902f93930dc6f627c4dd5da2703333287081c85cace0fc6e21c25af \
|
||||
--hash=sha256:304e7e2418146ae4d0ef0e9ffa28f881f7874b45b4994cc2279b21b6e7ae50c8 \
|
||||
--hash=sha256:32e5f3b8e32918bfbdd12eca62e49ab3031125c454b507127ad6ecbd86e62fca \
|
||||
--hash=sha256:334b79ce9c08f26b4659a53f42892793948a613c46f1b583e985fd5a6bf1c149 \
|
||||
--hash=sha256:33d19f0cde6838c81acffff25c7708e4adc7dd02896c9ec25c3939b1500a1778 \
|
||||
--hash=sha256:3799e36d60a35162bb35b2246d8bb012192b7437dff807ef79c14e7352706306 \
|
||||
--hash=sha256:42be5de7cc8c1edac55db92d82b68dc8e683b204d6f5414c5a51997a323d7081 \
|
||||
--hash=sha256:44b3267cea873684af022822195298501568ed44d542f9a2d9bebc0212e99069 \
|
||||
--hash=sha256:458d68d34fb74b906709735c927c029e62f7d06437a98af1b5b6258025223210 \
|
||||
--hash=sha256:45cc13d398b6359a7708986386f72bd156ae781c3e83a68a6d4cee5af04b1ce9 \
|
||||
--hash=sha256:4e7eaf9df15423d07b6050fb91f86c66307171b95ea53e2d87a7993b6d02c7f7 \
|
||||
--hash=sha256:4fad420b14ae1970a1f322e8ae84a1d9d89375eb71e1b504060ab2d1bfe68f3c \
|
||||
--hash=sha256:504b5116e2bd1821efd815941edff7535e93372a098e156bb9dffde30264e798 \
|
||||
--hash=sha256:50e7e96a527488334379e05755b210b7da4a60fc5d6481938c1fa053e0c92184 \
|
||||
--hash=sha256:51d27844763c273a122e08a3e86e7aefa54ee09fb672d96a645ece0454d8425e \
|
||||
--hash=sha256:5253dcb0bfda7214523de58b002eb0090cb530d7c55993ce5f6d17faf953ece7 \
|
||||
--hash=sha256:534efd2653ebc4f26fc0e47234e53bf0cb4715bb61f98c64d2774a278b58c846 \
|
||||
--hash=sha256:560278c9975694e1f0bc50da187abf2cdc1e4890739ea33df2bc4a85eeef143e \
|
||||
--hash=sha256:571452362d552de508c37191b6abbbb660028b8b418e2d68c20779e0bc8eaaa8 \
|
||||
--hash=sha256:62b5f7910b639f3c1d122d408421317c351e213ca39c964ad4121f27916631c6 \
|
||||
--hash=sha256:696639a73ca78a380acfaa0a1f6dd8220616a99074c05bba9ba8bb916914b224 \
|
||||
--hash=sha256:6ccdeef4584450b6f0bddd5135354908dacad95425fcb629fe36d13e48b60f32 \
|
||||
--hash=sha256:70364a097437dd0a90b31cd77f09f7387ad9ac60ef57590971f43b7fca3082a5 \
|
||||
--hash=sha256:7117cb7d6ac7f2e985f3d18aa8a1728864097da1a677ffa69e970ca215baebf1 \
|
||||
--hash=sha256:7467ad8b0eac0b28e52679e972b9b234b3de0ea5cee12eb50091d2b68145fe36 \
|
||||
--hash=sha256:7d35d4cc9270944e95f9c88af757b0c9fc43f396917e143a5756608462c5223b \
|
||||
--hash=sha256:7dda3091838206969c2b286f9832dff41e2da545b99d1cfaea9ebd8584d02708 \
|
||||
--hash=sha256:853cc36e756ff673bf984e9044ccc8fad60b95a748915dddeab9488aea974c73 \
|
||||
--hash=sha256:8722f72068b3e1156a4b2e1afde6810f1fc67155a9fa30a4b9d5b4bc46f18fb0 \
|
||||
--hash=sha256:8c6c71cf92b09e5faa72ea2c68aa1f61c9ce11cb66fdc5069d712f4392ddfd00 \
|
||||
--hash=sha256:903350bf44d7e4116b4d5898b30b15755d61dcd3161e3413a49c7db76f0bee5a \
|
||||
--hash=sha256:91b53dea84415e8115506cc62e441a2b54537359c63d856d73cb1abe05af4c9a \
|
||||
--hash=sha256:951be1eae7b47660412dc4938777a975ebc41936d64e28081bf2e584b47ec246 \
|
||||
--hash=sha256:972b49f2fe1047b9249c958ec4fa1bdd2cf8ce305dc19d27546d5a38e57732d8 \
|
||||
--hash=sha256:9a8625849387b9d558d528e263ecc9c0fbde86cfa5c2f0eef43fff480ae24d71 \
|
||||
--hash=sha256:9cdbb1998da94607d5eec02566b9586f0e70d6438abf1b690261aac0edda7ab6 \
|
||||
--hash=sha256:9e6d4d6ae1827b2f8c7200aaf7501c37cf3f3896c86a6aaf2566448397c823dd \
|
||||
--hash=sha256:aab65121229c2ecdf4a31b793d99a6a0501225bd39b616e653c87b219ed34a49 \
|
||||
--hash=sha256:ab98016541543692a37905871a5ffca59b16e08aacc3d7d10a27297b443f572d \
|
||||
--hash=sha256:ad45f3bccfcb00868f2871dce02a755529838d2b86163ab8a246115e80cfb7d6 \
|
||||
--hash=sha256:b43b78f9386d3d932a6ce5af4b45f393d2e93693ee18dc4800d30a8909df700e \
|
||||
--hash=sha256:b66421f8878a0c82fc0c272a43e2121c8d4c67cb37429b764f0d5ad70b82993b \
|
||||
--hash=sha256:ba034c8db4b264ef1601eb33cd23d87c5013b8fb48b8161debe2e5d3bd9156b0 \
|
||||
--hash=sha256:bbdc5db2c98ac2bf1971ffa1410c87ca7a15800415f788971e8ba8520fc0fda9 \
|
||||
--hash=sha256:bc0db93ad039fc2fe32ccd3dd0e0e70c4f3d6e37ae83f0a487e1aba939bd2fbd \
|
||||
--hash=sha256:bf7c8ee4861d9ef5b1120abb75846828c811f932d63311596ad25fa168053e00 \
|
||||
--hash=sha256:bf9596cba92ce7b1fd32c7b07c6e3212c7eed0edc271757e48bfcd2b54646452 \
|
||||
--hash=sha256:c43395a3b7cc9862801a65c6994678484f186ce13c929abab44fb8a9e473a55a \
|
||||
--hash=sha256:c46a76a599fcbf95f98755275c5527304cc4f1bb69919434c1e15544d7052910 \
|
||||
--hash=sha256:ca23b41355ba95929e9505ee04e55495726aa2282003ed9b012d86f857d3e49b \
|
||||
--hash=sha256:cd832bd9b6120d6074f39bdfbb3c80e416848b07ac72910f1c7f03131a6debc3 \
|
||||
--hash=sha256:cfa6d61a76c77610ba9274c1a90a453062bdf6887858afbe214d18ad41cf6bde \
|
||||
--hash=sha256:d8a0f0ab5453e409586b11ebe91c672040bc804ca98d03a656825f7890cbdf88 \
|
||||
--hash=sha256:e91b1976358e17197157b405cab408a5f4e33310cda211c49fc6da7cffd0b2f0 \
|
||||
--hash=sha256:ea057306ab469130167014b662643cfaed84651c792948891d003cf0039223a5 \
|
||||
--hash=sha256:eda3dd46df535da787ffb9036b5140f941ecb91701717df91c9daf64cabef953 \
|
||||
--hash=sha256:f03b1dbd4d9596dd84955bb40f7d885204d6aac0d56a919bb1e0ff2fb7e1735a \
|
||||
--hash=sha256:fa9335674d7c819674467c7b46154196c51efbaf5f5715187fd366814ba3fa39
|
||||
regex==2024.5.15 \
|
||||
--hash=sha256:0721931ad5fe0dda45d07f9820b90b2148ccdd8e45bb9e9b42a146cb4f695649 \
|
||||
--hash=sha256:10002e86e6068d9e1c91eae8295ef690f02f913c57db120b58fdd35a6bb1af35 \
|
||||
--hash=sha256:10e4ce0dca9ae7a66e6089bb29355d4432caed736acae36fef0fdd7879f0b0cb \
|
||||
--hash=sha256:119af6e56dce35e8dfb5222573b50c89e5508d94d55713c75126b753f834de68 \
|
||||
--hash=sha256:1337b7dbef9b2f71121cdbf1e97e40de33ff114801263b275aafd75303bd62b5 \
|
||||
--hash=sha256:13cdaf31bed30a1e1c2453ef6015aa0983e1366fad2667657dbcac7b02f67133 \
|
||||
--hash=sha256:1595f2d10dff3d805e054ebdc41c124753631b6a471b976963c7b28543cf13b0 \
|
||||
--hash=sha256:16093f563098448ff6b1fa68170e4acbef94e6b6a4e25e10eae8598bb1694b5d \
|
||||
--hash=sha256:1878b8301ed011704aea4c806a3cadbd76f84dece1ec09cc9e4dc934cfa5d4da \
|
||||
--hash=sha256:19068a6a79cf99a19ccefa44610491e9ca02c2be3305c7760d3831d38a467a6f \
|
||||
--hash=sha256:19dfb1c504781a136a80ecd1fff9f16dddf5bb43cec6871778c8a907a085bb3d \
|
||||
--hash=sha256:1b5269484f6126eee5e687785e83c6b60aad7663dafe842b34691157e5083e53 \
|
||||
--hash=sha256:1c1c174d6ec38d6c8a7504087358ce9213d4332f6293a94fbf5249992ba54efa \
|
||||
--hash=sha256:2431b9e263af1953c55abbd3e2efca67ca80a3de8a0437cb58e2421f8184717a \
|
||||
--hash=sha256:287eb7f54fc81546346207c533ad3c2c51a8d61075127d7f6d79aaf96cdee890 \
|
||||
--hash=sha256:2b4c884767504c0e2401babe8b5b7aea9148680d2e157fa28f01529d1f7fcf67 \
|
||||
--hash=sha256:35cb514e137cb3488bce23352af3e12fb0dbedd1ee6e60da053c69fb1b29cc6c \
|
||||
--hash=sha256:391d7f7f1e409d192dba8bcd42d3e4cf9e598f3979cdaed6ab11288da88cb9f2 \
|
||||
--hash=sha256:3ad070b823ca5890cab606c940522d05d3d22395d432f4aaaf9d5b1653e47ced \
|
||||
--hash=sha256:3cd7874d57f13bf70078f1ff02b8b0aa48d5b9ed25fc48547516c6aba36f5741 \
|
||||
--hash=sha256:3e507ff1e74373c4d3038195fdd2af30d297b4f0950eeda6f515ae3d84a1770f \
|
||||
--hash=sha256:455705d34b4154a80ead722f4f185b04c4237e8e8e33f265cd0798d0e44825fa \
|
||||
--hash=sha256:4a605586358893b483976cffc1723fb0f83e526e8f14c6e6614e75919d9862cf \
|
||||
--hash=sha256:4babf07ad476aaf7830d77000874d7611704a7fcf68c9c2ad151f5d94ae4bfc4 \
|
||||
--hash=sha256:4eee78a04e6c67e8391edd4dad3279828dd66ac4b79570ec998e2155d2e59fd5 \
|
||||
--hash=sha256:5397de3219a8b08ae9540c48f602996aa6b0b65d5a61683e233af8605c42b0f2 \
|
||||
--hash=sha256:5b5467acbfc153847d5adb21e21e29847bcb5870e65c94c9206d20eb4e99a384 \
|
||||
--hash=sha256:5eaa7ddaf517aa095fa8da0b5015c44d03da83f5bd49c87961e3c997daed0de7 \
|
||||
--hash=sha256:632b01153e5248c134007209b5c6348a544ce96c46005d8456de1d552455b014 \
|
||||
--hash=sha256:64c65783e96e563103d641760664125e91bd85d8e49566ee560ded4da0d3e704 \
|
||||
--hash=sha256:64f18a9a3513a99c4bef0e3efd4c4a5b11228b48aa80743be822b71e132ae4f5 \
|
||||
--hash=sha256:673b5a6da4557b975c6c90198588181029c60793835ce02f497ea817ff647cb2 \
|
||||
--hash=sha256:68811ab14087b2f6e0fc0c2bae9ad689ea3584cad6917fc57be6a48bbd012c49 \
|
||||
--hash=sha256:6e8d717bca3a6e2064fc3a08df5cbe366369f4b052dcd21b7416e6d71620dca1 \
|
||||
--hash=sha256:71a455a3c584a88f654b64feccc1e25876066c4f5ef26cd6dd711308aa538694 \
|
||||
--hash=sha256:72d7a99cd6b8f958e85fc6ca5b37c4303294954eac1376535b03c2a43eb72629 \
|
||||
--hash=sha256:7b59138b219ffa8979013be7bc85bb60c6f7b7575df3d56dc1e403a438c7a3f6 \
|
||||
--hash=sha256:7dbe2467273b875ea2de38ded4eba86cbcbc9a1a6d0aa11dcf7bd2e67859c435 \
|
||||
--hash=sha256:833616ddc75ad595dee848ad984d067f2f31be645d603e4d158bba656bbf516c \
|
||||
--hash=sha256:87e2a9c29e672fc65523fb47a90d429b70ef72b901b4e4b1bd42387caf0d6835 \
|
||||
--hash=sha256:8fe45aa3f4aa57faabbc9cb46a93363edd6197cbc43523daea044e9ff2fea83e \
|
||||
--hash=sha256:9e717956dcfd656f5055cc70996ee2cc82ac5149517fc8e1b60261b907740201 \
|
||||
--hash=sha256:9efa1a32ad3a3ea112224897cdaeb6aa00381627f567179c0314f7b65d354c62 \
|
||||
--hash=sha256:9ff11639a8d98969c863d4617595eb5425fd12f7c5ef6621a4b74b71ed8726d5 \
|
||||
--hash=sha256:a094801d379ab20c2135529948cb84d417a2169b9bdceda2a36f5f10977ebc16 \
|
||||
--hash=sha256:a0981022dccabca811e8171f913de05720590c915b033b7e601f35ce4ea7019f \
|
||||
--hash=sha256:a0bd000c6e266927cb7a1bc39d55be95c4b4f65c5be53e659537537e019232b1 \
|
||||
--hash=sha256:a32b96f15c8ab2e7d27655969a23895eb799de3665fa94349f3b2fbfd547236f \
|
||||
--hash=sha256:a81e3cfbae20378d75185171587cbf756015ccb14840702944f014e0d93ea09f \
|
||||
--hash=sha256:ac394ff680fc46b97487941f5e6ae49a9f30ea41c6c6804832063f14b2a5a145 \
|
||||
--hash=sha256:ada150c5adfa8fbcbf321c30c751dc67d2f12f15bd183ffe4ec7cde351d945b3 \
|
||||
--hash=sha256:b2b6f1b3bb6f640c1a92be3bbfbcb18657b125b99ecf141fb3310b5282c7d4ed \
|
||||
--hash=sha256:b802512f3e1f480f41ab5f2cfc0e2f761f08a1f41092d6718868082fc0d27143 \
|
||||
--hash=sha256:ba68168daedb2c0bab7fd7e00ced5ba90aebf91024dea3c88ad5063c2a562cca \
|
||||
--hash=sha256:bfc4f82cabe54f1e7f206fd3d30fda143f84a63fe7d64a81558d6e5f2e5aaba9 \
|
||||
--hash=sha256:c0c18345010870e58238790a6779a1219b4d97bd2e77e1140e8ee5d14df071aa \
|
||||
--hash=sha256:c3bea0ba8b73b71b37ac833a7f3fd53825924165da6a924aec78c13032f20850 \
|
||||
--hash=sha256:c486b4106066d502495b3025a0a7251bf37ea9540433940a23419461ab9f2a80 \
|
||||
--hash=sha256:c49e15eac7c149f3670b3e27f1f28a2c1ddeccd3a2812cba953e01be2ab9b5fe \
|
||||
--hash=sha256:c6a2b494a76983df8e3d3feea9b9ffdd558b247e60b92f877f93a1ff43d26656 \
|
||||
--hash=sha256:cab12877a9bdafde5500206d1020a584355a97884dfd388af3699e9137bf7388 \
|
||||
--hash=sha256:cac27dcaa821ca271855a32188aa61d12decb6fe45ffe3e722401fe61e323cd1 \
|
||||
--hash=sha256:cdd09d47c0b2efee9378679f8510ee6955d329424c659ab3c5e3a6edea696294 \
|
||||
--hash=sha256:cf2430df4148b08fb4324b848672514b1385ae3807651f3567871f130a728cc3 \
|
||||
--hash=sha256:d0a3d8d6acf0c78a1fff0e210d224b821081330b8524e3e2bc5a68ef6ab5803d \
|
||||
--hash=sha256:d0c0c0003c10f54a591d220997dd27d953cd9ccc1a7294b40a4be5312be8797b \
|
||||
--hash=sha256:d1f059a4d795e646e1c37665b9d06062c62d0e8cc3c511fe01315973a6542e40 \
|
||||
--hash=sha256:d347a741ea871c2e278fde6c48f85136c96b8659b632fb57a7d1ce1872547600 \
|
||||
--hash=sha256:d3ee02d9e5f482cc8309134a91eeaacbdd2261ba111b0fef3748eeb4913e6a2c \
|
||||
--hash=sha256:d99ceffa25ac45d150e30bd9ed14ec6039f2aad0ffa6bb87a5936f5782fc1569 \
|
||||
--hash=sha256:e38a7d4e8f633a33b4c7350fbd8bad3b70bf81439ac67ac38916c4a86b465456 \
|
||||
--hash=sha256:e4682f5ba31f475d58884045c1a97a860a007d44938c4c0895f41d64481edbc9 \
|
||||
--hash=sha256:e5bb9425fe881d578aeca0b2b4b3d314ec88738706f66f219c194d67179337cb \
|
||||
--hash=sha256:e64198f6b856d48192bf921421fdd8ad8eb35e179086e99e99f711957ffedd6e \
|
||||
--hash=sha256:e6662686aeb633ad65be2a42b4cb00178b3fbf7b91878f9446075c404ada552f \
|
||||
--hash=sha256:ec54d5afa89c19c6dd8541a133be51ee1017a38b412b1321ccb8d6ddbeb4cf7d \
|
||||
--hash=sha256:f5b1dff3ad008dccf18e652283f5e5339d70bf8ba7c98bf848ac33db10f7bc7a \
|
||||
--hash=sha256:f8ec0c2fea1e886a19c3bee0cd19d862b3aa75dcdfb42ebe8ed30708df64687a \
|
||||
--hash=sha256:f9ebd0a36102fcad2f03696e8af4ae682793a5d30b46c647eaf280d6cfb32796
|
||||
# via mkdocs-material
|
||||
requests==2.31.0 \
|
||||
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
|
||||
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
|
||||
requests==2.32.2 \
|
||||
--hash=sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289 \
|
||||
--hash=sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c
|
||||
# via
|
||||
# importlib-metadata
|
||||
# importlib-resources
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
setuptools==69.5.1 \
|
||||
--hash=sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987 \
|
||||
--hash=sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32
|
||||
setuptools==70.0.0 \
|
||||
--hash=sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4 \
|
||||
--hash=sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0
|
||||
# via mkdocs-material
|
||||
six==1.16.0 \
|
||||
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
|
||||
|
|
@ -682,7 +682,7 @@ webencodings==0.5.1 \
|
|||
# via
|
||||
# cssselect2
|
||||
# tinycss2
|
||||
zipp==3.18.1 \
|
||||
--hash=sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b \
|
||||
--hash=sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715
|
||||
zipp==3.18.2 \
|
||||
--hash=sha256:6278d9ddbcfb1f1089a88fde84481528b07b0e10474e09dcfe53dad4069fa059 \
|
||||
--hash=sha256:dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e
|
||||
# via pytablewriter
|
||||
|
|
|
|||
|
|
@ -241,7 +241,7 @@ ModSecurity is integrated and enabled by default alongside the OWASP Core Rule S
|
|||
| :-----------------------: | :-----: | :---------------------------------------------------------------------------------------------------- |
|
||||
| `USE_MODSECURITY` | `yes` | When set to `yes`, ModSecurity will be enabled. |
|
||||
| `USE_MODSECURITY_CRS` | `yes` | When set to `yes` and `USE_MODSECURITY` is also set to `yes`, the OWASP Core Rule Set will be loaded. |
|
||||
| `MODSECURITY_CRS_VERSION` | `3` | Version of the OWASP Core Rule Set to use. |
|
||||
| `MODSECURITY_CRS_VERSION` | `3` | Version of the OWASP Core Rule Set to use with ModSecurity (3, 4 or nightly). |
|
||||
|
||||
!!! warning "ModSecurity and the OWASP Core Rule Set"
|
||||
**We strongly recommend keeping both ModSecurity and the OWASP Core Rule Set enabled**. The only downsides are the false positives that may occur. But they can be fixed with some efforts and the CRS team maintains a list of exclusions for common applications (e.g., WordPress, Nextcloud, Drupal, Cpanel, ...).
|
||||
|
|
@ -250,6 +250,10 @@ You can choose between the following versions of the OWASP Core Rule Set :
|
|||
|
||||
- **3** : The version [v3.3.5](https://github.com/coreruleset/coreruleset/releases/tag/v3.3.5) of the OWASP Core Rule Set (***default***)
|
||||
- **4** : The version [v4.2.0](https://github.com/coreruleset/coreruleset/releases/tag/v4.2.0) of the OWASP Core Rule Set
|
||||
- **nightly** : The latest [nightly](https://github.com/coreruleset/coreruleset/releases/tag/nightly) build of the OWASP Core Rule Set which is updated every day
|
||||
|
||||
!!! example "OWASP Core Rule Set's nightly build"
|
||||
The nightly build of the OWASP Core Rule Set is updated every day and contains the latest rules. It is recommended to use it in a staging environment before using it in production.
|
||||
|
||||
### Custom configurations
|
||||
|
||||
|
|
|
|||
|
|
@ -253,10 +253,11 @@ STREAM support :white_check_mark:
|
|||
|
||||
Integrate easily the Database.
|
||||
|
||||
| Setting | Default |Context|Multiple| Description |
|
||||
|--------------------|-----------------------------------------|-------|--------|--------------------------------------------------|
|
||||
|`DATABASE_URI` |`sqlite:////var/lib/bunkerweb/db.sqlite3`|global |no |The database URI, following the sqlalchemy format.|
|
||||
|`DATABASE_LOG_LEVEL`|`warning` |global |no |The level to use for database logs. |
|
||||
| Setting | Default |Context|Multiple| Description |
|
||||
|-----------------------|-----------------------------------------|-------|--------|-----------------------------------------------------------------------------------------------------------------------------------------|
|
||||
|`DATABASE_URI` |`sqlite:////var/lib/bunkerweb/db.sqlite3`|global |no |The database URI, following the sqlalchemy format. |
|
||||
|`DATABASE_URI_READONLY`| |global |no |The database URI for read-only operations, it can also serve as a fallback if the main database is down. Following the sqlalchemy format.|
|
||||
|`DATABASE_LOG_LEVEL` |`warning` |global |no |The level to use for database logs. |
|
||||
|
||||
## DNSBL
|
||||
|
||||
|
|
@ -368,16 +369,16 @@ STREAM support :white_check_mark:
|
|||
|
||||
Automatic creation, renewal and configuration of Let's Encrypt certificates using DNS challenges.
|
||||
|
||||
| Setting | Default | Context |Multiple| Description |
|
||||
|----------------------------------|---------|---------|--------|---------------------------------------------------------------------------------------|
|
||||
|`AUTO_LETS_ENCRYPT_DNS` |`no` |multisite|no |Activate automatic Let's Encrypt DNS. |
|
||||
|`LETS_ENCRYPT_DNS_EMAIL` | |multisite|no |The email address to use for Let's Encrypt notifications. |
|
||||
|`USE_LETS_ENCRYPT_DNS_STAGING` |`no` |multisite|no |Use the Let's Encrypt staging environment. |
|
||||
|`LETS_ENCRYPT_DNS_PROVIDER` | |multisite|no |The DNS provider to use for DNS challenges. |
|
||||
|`USE_LETS_ENCRYPT_DNS_WILDCARD` |`yes` |multisite|no |Create wildcard certificates for all domains using DNS challenges. |
|
||||
|`LETS_ENCRYPT_DNS_PROPAGATION` |`default`|multisite|no |The time to wait for DNS propagation in seconds. |
|
||||
|`LETS_ENCRYPT_DNS_CREDENTIAL_ITEM`| |multisite|yes |Configuration item that will be added to the credentials.ini file for the DNS provider.|
|
||||
|`LETS_ENCRYPT_DNS_CLEAR_OLD_CERTS`|`no` |global |no |Clear old certificates when renewing. |
|
||||
| Setting | Default | Context |Multiple| Description |
|
||||
|----------------------------------|---------|---------|--------|----------------------------------------------------------------------------------------------------------------------------|
|
||||
|`AUTO_LETS_ENCRYPT_DNS` |`no` |multisite|no |Activate automatic Let's Encrypt DNS. |
|
||||
|`LETS_ENCRYPT_DNS_EMAIL` | |multisite|no |The email address to use for Let's Encrypt notifications. |
|
||||
|`USE_LETS_ENCRYPT_DNS_STAGING` |`no` |multisite|no |Use the Let's Encrypt staging environment. |
|
||||
|`LETS_ENCRYPT_DNS_PROVIDER` | |multisite|no |The DNS provider to use for DNS challenges. |
|
||||
|`USE_LETS_ENCRYPT_DNS_WILDCARD` |`yes` |multisite|no |Create wildcard certificates for all domains using DNS challenges. |
|
||||
|`LETS_ENCRYPT_DNS_PROPAGATION` |`default`|multisite|no |The time to wait for DNS propagation in seconds. |
|
||||
|`LETS_ENCRYPT_DNS_CREDENTIAL_ITEM`| |multisite|yes |Configuration item that will be added to the credentials.ini file for the DNS provider (e.g. 'cloudflare_api_token 123456').|
|
||||
|`LETS_ENCRYPT_DNS_CLEAR_OLD_CERTS`|`no` |global |no |Clear old certificates when renewing. |
|
||||
|
||||
## Limit
|
||||
|
||||
|
|
@ -448,14 +449,14 @@ STREAM support :x:
|
|||
|
||||
Management of the ModSecurity WAF.
|
||||
|
||||
| Setting | Default | Context |Multiple| Description |
|
||||
|---------------------------------|--------------|---------|--------|------------------------------------------|
|
||||
|`USE_MODSECURITY` |`yes` |multisite|no |Enable ModSecurity WAF. |
|
||||
|`USE_MODSECURITY_CRS` |`yes` |multisite|no |Enable OWASP Core Rule Set. |
|
||||
|`MODSECURITY_CRS_VERSION` |`3` |multisite|no |Version of the OWASP Core Rule Set to use.|
|
||||
|`MODSECURITY_SEC_AUDIT_ENGINE` |`RelevantOnly`|multisite|no |SecAuditEngine directive of ModSecurity. |
|
||||
|`MODSECURITY_SEC_RULE_ENGINE` |`On` |multisite|no |SecRuleEngine directive of ModSecurity. |
|
||||
|`MODSECURITY_SEC_AUDIT_LOG_PARTS`|`ABCFHZ` |multisite|no |SecAuditLogParts directive of ModSecurity.|
|
||||
| Setting | Default | Context |Multiple| Description |
|
||||
|---------------------------------|--------------|---------|--------|-----------------------------------------------------------------------------|
|
||||
|`USE_MODSECURITY` |`yes` |multisite|no |Enable ModSecurity WAF. |
|
||||
|`USE_MODSECURITY_CRS` |`yes` |multisite|no |Enable OWASP Core Rule Set. |
|
||||
|`MODSECURITY_CRS_VERSION` |`3` |multisite|no |Version of the OWASP Core Rule Set to use with ModSecurity (3, 4 or nightly).|
|
||||
|`MODSECURITY_SEC_AUDIT_ENGINE` |`RelevantOnly`|multisite|no |SecAuditEngine directive of ModSecurity. |
|
||||
|`MODSECURITY_SEC_RULE_ENGINE` |`On` |multisite|no |SecRuleEngine directive of ModSecurity. |
|
||||
|`MODSECURITY_SEC_AUDIT_LOG_PARTS`|`ABCFHZ` |multisite|no |SecAuditLogParts directive of ModSecurity. |
|
||||
|
||||
## Monitoring <img src='../assets/img/pro-icon.svg' alt='crow pro icon' height='24px' width='24px' style='transform : translateY(3px);'> (PRO)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,236 +1,232 @@
|
|||
{
|
||||
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"location": {
|
||||
"type": "string"
|
||||
},
|
||||
"networkInterfaceName1": {
|
||||
"type": "string"
|
||||
},
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string"
|
||||
},
|
||||
"networkSecurityGroupRules": {
|
||||
"type": "array"
|
||||
},
|
||||
"subnetName": {
|
||||
"type": "string"
|
||||
},
|
||||
"virtualNetworkName": {
|
||||
"type": "string"
|
||||
},
|
||||
"addressPrefixes": {
|
||||
"type": "array"
|
||||
},
|
||||
"subnets": {
|
||||
"type": "array"
|
||||
},
|
||||
"publicIpAddressName1": {
|
||||
"type": "string"
|
||||
},
|
||||
"publicIpAddressType": {
|
||||
"type": "string"
|
||||
},
|
||||
"publicIpAddressSku": {
|
||||
"type": "string"
|
||||
},
|
||||
"pipDeleteOption": {
|
||||
"type": "string"
|
||||
},
|
||||
"virtualMachineName": {
|
||||
"type": "string"
|
||||
},
|
||||
"virtualMachineName1": {
|
||||
"type": "string"
|
||||
},
|
||||
"virtualMachineComputerName1": {
|
||||
"type": "string"
|
||||
},
|
||||
"virtualMachineRG": {
|
||||
"type": "string"
|
||||
},
|
||||
"osDiskType": {
|
||||
"type": "string"
|
||||
},
|
||||
"osDiskDeleteOption": {
|
||||
"type": "string"
|
||||
},
|
||||
"virtualMachineSize": {
|
||||
"type": "string"
|
||||
},
|
||||
"nicDeleteOption": {
|
||||
"type": "string"
|
||||
},
|
||||
"hibernationEnabled": {
|
||||
"type": "bool"
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string"
|
||||
},
|
||||
"securityType": {
|
||||
"type": "string"
|
||||
},
|
||||
"secureBoot": {
|
||||
"type": "bool"
|
||||
},
|
||||
"vTPM": {
|
||||
"type": "bool"
|
||||
},
|
||||
"virtualMachine1Zone": {
|
||||
"type": "string"
|
||||
}
|
||||
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
|
||||
"contentVersion": "1.0.0.0",
|
||||
"parameters": {
|
||||
"location": {
|
||||
"type": "string"
|
||||
},
|
||||
"variables": {
|
||||
"nsgId": "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]",
|
||||
"vnetName": "[parameters('virtualNetworkName')]",
|
||||
"vnetId": "[resourceId(resourceGroup().name,'Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]",
|
||||
"subnetRef": "[concat(variables('vnetId'), '/subnets/', parameters('subnetName'))]"
|
||||
"networkInterfaceName1": {
|
||||
"type": "string"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"name": "[parameters('networkInterfaceName1')]",
|
||||
"type": "Microsoft.Network/networkInterfaces",
|
||||
"apiVersion": "2022-11-01",
|
||||
"location": "[parameters('location')]",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName'))]",
|
||||
"[concat('Microsoft.Network/virtualNetworks/', parameters('virtualNetworkName'))]",
|
||||
"[concat('Microsoft.Network/publicIpAddresses/', parameters('publicIpAddressName1'))]"
|
||||
],
|
||||
"properties": {
|
||||
"ipConfigurations": [
|
||||
{
|
||||
"name": "ipconfig1",
|
||||
"properties": {
|
||||
"subnet": {
|
||||
"id": "[variables('subnetRef')]"
|
||||
},
|
||||
"privateIPAllocationMethod": "Dynamic",
|
||||
"publicIpAddress": {
|
||||
"id": "[resourceId(resourceGroup().name, 'Microsoft.Network/publicIpAddresses', parameters('publicIpAddressName1'))]",
|
||||
"properties": {
|
||||
"deleteOption": "[parameters('pipDeleteOption')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"networkSecurityGroup": {
|
||||
"id": "[variables('nsgId')]"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "[parameters('networkSecurityGroupName')]",
|
||||
"type": "Microsoft.Network/networkSecurityGroups",
|
||||
"apiVersion": "2020-05-01",
|
||||
"location": "[parameters('location')]",
|
||||
"properties": {
|
||||
"securityRules": "[parameters('networkSecurityGroupRules')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "[parameters('virtualNetworkName')]",
|
||||
"type": "Microsoft.Network/virtualNetworks",
|
||||
"apiVersion": "2023-02-01",
|
||||
"location": "[parameters('location')]",
|
||||
"properties": {
|
||||
"addressSpace": {
|
||||
"addressPrefixes": "[parameters('addressPrefixes')]"
|
||||
},
|
||||
"subnets": "[parameters('subnets')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "[parameters('publicIpAddressName1')]",
|
||||
"type": "Microsoft.Network/publicIpAddresses",
|
||||
"apiVersion": "2020-08-01",
|
||||
"location": "[parameters('location')]",
|
||||
"properties": {
|
||||
"publicIpAllocationMethod": "[parameters('publicIpAddressType')]"
|
||||
},
|
||||
"sku": {
|
||||
"name": "[parameters('publicIpAddressSku')]"
|
||||
},
|
||||
"zones": [
|
||||
"[parameters('virtualMachine1Zone')]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "[parameters('virtualMachineName1')]",
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"apiVersion": "2024-03-01",
|
||||
"location": "[parameters('location')]",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkInterfaces/', parameters('networkInterfaceName1'))]"
|
||||
],
|
||||
"properties": {
|
||||
"hardwareProfile": {
|
||||
"vmSize": "[parameters('virtualMachineSize')]"
|
||||
},
|
||||
"storageProfile": {
|
||||
"osDisk": {
|
||||
"createOption": "fromImage",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[parameters('osDiskType')]"
|
||||
},
|
||||
"deleteOption": "[parameters('osDiskDeleteOption')]"
|
||||
},
|
||||
"imageReference": {
|
||||
"publisher": "bunkerity",
|
||||
"offer": "bunkerweb",
|
||||
"sku": "bunkerweb",
|
||||
"version": "latest"
|
||||
}
|
||||
},
|
||||
"networkProfile": {
|
||||
"networkInterfaces": [
|
||||
{
|
||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('networkInterfaceName1'))]",
|
||||
"properties": {
|
||||
"deleteOption": "[parameters('nicDeleteOption')]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"additionalCapabilities": {
|
||||
"hibernationEnabled": false
|
||||
},
|
||||
"osProfile": {
|
||||
"computerName": "[parameters('virtualMachineComputerName1')]",
|
||||
"adminUsername": "[parameters('adminUsername')]",
|
||||
"linuxConfiguration": {
|
||||
"disablePasswordAuthentication": true
|
||||
}
|
||||
},
|
||||
"securityProfile": {
|
||||
"securityType": "[parameters('securityType')]",
|
||||
"uefiSettings": {
|
||||
"secureBootEnabled": "[parameters('secureBoot')]",
|
||||
"vTpmEnabled": "[parameters('vTPM')]"
|
||||
}
|
||||
},
|
||||
"diagnosticsProfile": {
|
||||
"bootDiagnostics": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"plan": {
|
||||
"name": "bunkerweb",
|
||||
"publisher": "bunkerity",
|
||||
"product": "bunkerweb"
|
||||
},
|
||||
"zones": [
|
||||
"[parameters('virtualMachine1Zone')]"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminUsername')]"
|
||||
}
|
||||
"networkSecurityGroupName": {
|
||||
"type": "string"
|
||||
},
|
||||
"networkSecurityGroupRules": {
|
||||
"type": "array"
|
||||
},
|
||||
"subnetName": {
|
||||
"type": "string"
|
||||
},
|
||||
"virtualNetworkName": {
|
||||
"type": "string"
|
||||
},
|
||||
"addressPrefixes": {
|
||||
"type": "array"
|
||||
},
|
||||
"subnets": {
|
||||
"type": "array"
|
||||
},
|
||||
"publicIpAddressName1": {
|
||||
"type": "string"
|
||||
},
|
||||
"publicIpAddressType": {
|
||||
"type": "string"
|
||||
},
|
||||
"publicIpAddressSku": {
|
||||
"type": "string"
|
||||
},
|
||||
"pipDeleteOption": {
|
||||
"type": "string"
|
||||
},
|
||||
"virtualMachineName": {
|
||||
"type": "string"
|
||||
},
|
||||
"virtualMachineName1": {
|
||||
"type": "string"
|
||||
},
|
||||
"virtualMachineComputerName1": {
|
||||
"type": "string"
|
||||
},
|
||||
"virtualMachineRG": {
|
||||
"type": "string"
|
||||
},
|
||||
"osDiskType": {
|
||||
"type": "string"
|
||||
},
|
||||
"osDiskDeleteOption": {
|
||||
"type": "string"
|
||||
},
|
||||
"virtualMachineSize": {
|
||||
"type": "string"
|
||||
},
|
||||
"nicDeleteOption": {
|
||||
"type": "string"
|
||||
},
|
||||
"hibernationEnabled": {
|
||||
"type": "bool"
|
||||
},
|
||||
"adminUsername": {
|
||||
"type": "string"
|
||||
},
|
||||
"securityType": {
|
||||
"type": "string"
|
||||
},
|
||||
"secureBoot": {
|
||||
"type": "bool"
|
||||
},
|
||||
"vTPM": {
|
||||
"type": "bool"
|
||||
},
|
||||
"virtualMachine1Zone": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"variables": {
|
||||
"nsgId": "[resourceId(resourceGroup().name, 'Microsoft.Network/networkSecurityGroups', parameters('networkSecurityGroupName'))]",
|
||||
"vnetName": "[parameters('virtualNetworkName')]",
|
||||
"vnetId": "[resourceId(resourceGroup().name,'Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]",
|
||||
"subnetRef": "[concat(variables('vnetId'), '/subnets/', parameters('subnetName'))]"
|
||||
},
|
||||
"resources": [
|
||||
{
|
||||
"name": "[parameters('networkInterfaceName1')]",
|
||||
"type": "Microsoft.Network/networkInterfaces",
|
||||
"apiVersion": "2022-11-01",
|
||||
"location": "[parameters('location')]",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkSecurityGroups/', parameters('networkSecurityGroupName'))]",
|
||||
"[concat('Microsoft.Network/virtualNetworks/', parameters('virtualNetworkName'))]",
|
||||
"[concat('Microsoft.Network/publicIpAddresses/', parameters('publicIpAddressName1'))]"
|
||||
],
|
||||
"properties": {
|
||||
"ipConfigurations": [
|
||||
{
|
||||
"name": "ipconfig1",
|
||||
"properties": {
|
||||
"subnet": {
|
||||
"id": "[variables('subnetRef')]"
|
||||
},
|
||||
"privateIPAllocationMethod": "Dynamic",
|
||||
"publicIpAddress": {
|
||||
"id": "[resourceId(resourceGroup().name, 'Microsoft.Network/publicIpAddresses', parameters('publicIpAddressName1'))]",
|
||||
"properties": {
|
||||
"deleteOption": "[parameters('pipDeleteOption')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"networkSecurityGroup": {
|
||||
"id": "[variables('nsgId')]"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "[parameters('networkSecurityGroupName')]",
|
||||
"type": "Microsoft.Network/networkSecurityGroups",
|
||||
"apiVersion": "2020-05-01",
|
||||
"location": "[parameters('location')]",
|
||||
"properties": {
|
||||
"securityRules": "[parameters('networkSecurityGroupRules')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "[parameters('virtualNetworkName')]",
|
||||
"type": "Microsoft.Network/virtualNetworks",
|
||||
"apiVersion": "2023-02-01",
|
||||
"location": "[parameters('location')]",
|
||||
"properties": {
|
||||
"addressSpace": {
|
||||
"addressPrefixes": "[parameters('addressPrefixes')]"
|
||||
},
|
||||
"subnets": "[parameters('subnets')]"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "[parameters('publicIpAddressName1')]",
|
||||
"type": "Microsoft.Network/publicIpAddresses",
|
||||
"apiVersion": "2020-08-01",
|
||||
"location": "[parameters('location')]",
|
||||
"properties": {
|
||||
"publicIpAllocationMethod": "[parameters('publicIpAddressType')]"
|
||||
},
|
||||
"sku": {
|
||||
"name": "[parameters('publicIpAddressSku')]"
|
||||
},
|
||||
"zones": ["[parameters('virtualMachine1Zone')]"]
|
||||
},
|
||||
{
|
||||
"name": "[parameters('virtualMachineName1')]",
|
||||
"type": "Microsoft.Compute/virtualMachines",
|
||||
"apiVersion": "2024-03-01",
|
||||
"location": "[parameters('location')]",
|
||||
"dependsOn": [
|
||||
"[concat('Microsoft.Network/networkInterfaces/', parameters('networkInterfaceName1'))]"
|
||||
],
|
||||
"properties": {
|
||||
"hardwareProfile": {
|
||||
"vmSize": "[parameters('virtualMachineSize')]"
|
||||
},
|
||||
"storageProfile": {
|
||||
"osDisk": {
|
||||
"createOption": "fromImage",
|
||||
"managedDisk": {
|
||||
"storageAccountType": "[parameters('osDiskType')]"
|
||||
},
|
||||
"deleteOption": "[parameters('osDiskDeleteOption')]"
|
||||
},
|
||||
"imageReference": {
|
||||
"publisher": "bunkerity",
|
||||
"offer": "bunkerweb",
|
||||
"sku": "bunkerweb",
|
||||
"version": "latest"
|
||||
}
|
||||
},
|
||||
"networkProfile": {
|
||||
"networkInterfaces": [
|
||||
{
|
||||
"id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('networkInterfaceName1'))]",
|
||||
"properties": {
|
||||
"deleteOption": "[parameters('nicDeleteOption')]"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"additionalCapabilities": {
|
||||
"hibernationEnabled": false
|
||||
},
|
||||
"osProfile": {
|
||||
"computerName": "[parameters('virtualMachineComputerName1')]",
|
||||
"adminUsername": "[parameters('adminUsername')]",
|
||||
"linuxConfiguration": {
|
||||
"disablePasswordAuthentication": true
|
||||
}
|
||||
},
|
||||
"securityProfile": {
|
||||
"securityType": "[parameters('securityType')]",
|
||||
"uefiSettings": {
|
||||
"secureBootEnabled": "[parameters('secureBoot')]",
|
||||
"vTpmEnabled": "[parameters('vTPM')]"
|
||||
}
|
||||
},
|
||||
"diagnosticsProfile": {
|
||||
"bootDiagnostics": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"plan": {
|
||||
"name": "bunkerweb",
|
||||
"publisher": "bunkerity",
|
||||
"product": "bunkerweb"
|
||||
},
|
||||
"zones": ["[parameters('virtualMachine1Zone')]"]
|
||||
}
|
||||
],
|
||||
"outputs": {
|
||||
"adminUsername": {
|
||||
"type": "string",
|
||||
"value": "[parameters('adminUsername')]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,8 @@ RUN apk add --no-cache bash && \
|
|||
chmod 660 INTEGRATION
|
||||
|
||||
# Fix CVEs
|
||||
# There are no CVEs to fix for this image
|
||||
RUN apk add --no-cache "busybox>=1.36.1-r17" "busybox-binsh>=1.36.1-r17" "ssl_client>=1.36.1-r17" # CVE-2023-42363 CVE-2023-42364 CVE-2023-42365 CVE-2023-42366
|
||||
RUN apk add --no-cache "libcrypto3>=3.1.5-r0" "libssl3>=3.1.5-r0" # CVE-2024-4603
|
||||
|
||||
LABEL maintainer "Bunkerity <contact@bunkerity.com>"
|
||||
LABEL version "1.5.7"
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ RUN apk add --no-cache openssl pcre bash python3 yajl geoip libxml2 libgd curl &
|
|||
ln -s /proc/1/fd/1 /var/log/bunkerweb/access.log
|
||||
|
||||
# Fix CVEs
|
||||
# There are no CVEs to fix for this image
|
||||
RUN apk add --no-cache "busybox>=1.35.0-r30" "busybox-binsh>=1.35.0-r30" "ssl_client>=1.35.0-r30" # CVE-2023-42366
|
||||
|
||||
LABEL maintainer "Bunkerity <contact@bunkerity.com>"
|
||||
LABEL version "1.5.7"
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -36,11 +36,40 @@ server {
|
|||
{% endif %}
|
||||
|
||||
{% if IS_LOADING == "yes" +%}
|
||||
root /usr/share/bunkerweb/loading;
|
||||
try_files /index.html =404;
|
||||
etag off;
|
||||
add_header Last-Modified "";
|
||||
server_tokens off;
|
||||
location / {
|
||||
etag off;
|
||||
add_header Last-Modified "";
|
||||
server_tokens off;
|
||||
default_type 'text/html';
|
||||
root /usr/share/bunkerweb/loading;
|
||||
content_by_lua_block {
|
||||
local utils = require "bunkerweb.utils"
|
||||
local rand = utils.rand
|
||||
local subsystem = ngx.config.subsystem
|
||||
|
||||
local template
|
||||
local render = nil
|
||||
if subsystem == "http" then
|
||||
template = require "resty.template"
|
||||
render = template.render
|
||||
end
|
||||
|
||||
local nonce_style = rand(16)
|
||||
|
||||
-- Override CSP header
|
||||
ngx.header["Content-Security-Policy"] = "default-src 'none'; form-action 'self'; img-src 'self' data:; style-src 'self' 'nonce-"
|
||||
.. nonce_style
|
||||
.. "'; font-src 'self' data:; base-uri 'self'; require-trusted-types-for 'script';"
|
||||
|
||||
-- Remove server header
|
||||
ngx.header["Server"] = nil
|
||||
|
||||
-- Render template
|
||||
render("index.html", {
|
||||
nonce_style = nonce_style
|
||||
})
|
||||
}
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
# include core and plugins default-server configurations
|
||||
|
|
|
|||
|
|
@ -11,7 +11,16 @@
|
|||
"help": "The database URI, following the sqlalchemy format.",
|
||||
"id": "database-uri",
|
||||
"label": "The database URI",
|
||||
"regex": "^(postgresql|mysql|mariadb|sqlite|oracle)(\\+[\\w\\-]+)?:.+$",
|
||||
"regex": "^((postgresql|mysql|mariadb|sqlite)(\\+[\\w\\-]+)?:.+)?$",
|
||||
"type": "text"
|
||||
},
|
||||
"DATABASE_URI_READONLY": {
|
||||
"context": "global",
|
||||
"default": "",
|
||||
"help": "The database URI for read-only operations, it can also serve as a fallback if the main database is down. Following the sqlalchemy format.",
|
||||
"id": "database-uri-readonly",
|
||||
"label": "The database URI for read-only operations",
|
||||
"regex": "^((postgresql|mysql|mariadb|sqlite)(\\+[\\w\\-]+)?:.+)?$",
|
||||
"type": "text"
|
||||
},
|
||||
"DATABASE_LOG_LEVEL": {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
local class = require "middleclass"
|
||||
local plugin = require "bunkerweb.plugin"
|
||||
local utils = require "bunkerweb.utils"
|
||||
|
||||
local ngx = ngx
|
||||
local rand = utils.rand
|
||||
local subsystem = ngx.config.subsystem
|
||||
local tostring = tostring
|
||||
|
||||
|
|
@ -84,12 +86,28 @@ function errors:log()
|
|||
end
|
||||
|
||||
function errors:render_template(code)
|
||||
local nonce_script = rand(16)
|
||||
local nonce_style = rand(16)
|
||||
|
||||
-- Override headers
|
||||
local header = "Content-Security-Policy"
|
||||
if self.variables["CONTENT_SECURITY_POLICY_REPORT_ONLY"] == "yes" then
|
||||
header = header .. "-Report-Only"
|
||||
end
|
||||
ngx.header[header] = "default-src 'none'; form-action 'self'; script-src 'strict-dynamic' 'nonce-"
|
||||
.. nonce_script
|
||||
.. "' 'unsafe-inline' http: https:; img-src 'self' data:; style-src 'self' 'nonce-"
|
||||
.. nonce_style
|
||||
.. "'; font-src 'self' data:; base-uri 'self'; require-trusted-types-for 'script';"
|
||||
|
||||
-- Render template
|
||||
render("error.html", {
|
||||
title = code .. " - " .. self.default_errors[code].title,
|
||||
error_title = self.default_errors[code].title,
|
||||
error_code = code,
|
||||
error_text = self.default_errors[code].text,
|
||||
nonce_script = nonce_script,
|
||||
nonce_style = nonce_style,
|
||||
})
|
||||
end
|
||||
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -9,7 +9,7 @@ from stat import S_IEXEC
|
|||
from sys import exit as sys_exit, path as sys_path
|
||||
from threading import Lock
|
||||
from uuid import uuid4
|
||||
from json import JSONDecodeError, loads
|
||||
from json import JSONDecodeError, load as json_load, loads
|
||||
from shutil import copytree, rmtree
|
||||
from tarfile import open as tar_open
|
||||
from traceback import format_exc
|
||||
|
|
@ -176,26 +176,27 @@ try:
|
|||
rmtree(plugin_path, ignore_errors=True)
|
||||
continue
|
||||
|
||||
plugin_file = loads(plugin_path.joinpath("plugin.json").read_text(encoding="utf-8"))
|
||||
|
||||
with BytesIO() as plugin_content:
|
||||
with tar_open(fileobj=plugin_content, mode="w:gz", compresslevel=9) as tar:
|
||||
tar.add(plugin_path, arcname=plugin_path.name)
|
||||
plugin_content.seek(0)
|
||||
value = plugin_content.getvalue()
|
||||
tar.add(plugin_path, arcname=plugin_path.name, recursive=True)
|
||||
plugin_content.seek(0, 0)
|
||||
|
||||
plugin_file.update(
|
||||
{
|
||||
"type": "external",
|
||||
"page": plugin_path.joinpath("ui").is_dir(),
|
||||
"method": "scheduler",
|
||||
"data": value,
|
||||
"checksum": bytes_hash(value, algorithm="sha256"),
|
||||
}
|
||||
)
|
||||
with plugin_path.joinpath("plugin.json").open("r", encoding="utf-8") as f:
|
||||
plugin_data = json_load(f)
|
||||
|
||||
external_plugins.append(plugin_file)
|
||||
external_plugins_ids.append(plugin_file["id"])
|
||||
checksum = bytes_hash(plugin_content, algorithm="sha256")
|
||||
plugin_data.update(
|
||||
{
|
||||
"type": "external",
|
||||
"page": plugin_path.joinpath("ui").is_dir(),
|
||||
"method": "scheduler",
|
||||
"data": plugin_content.getvalue(),
|
||||
"checksum": checksum,
|
||||
}
|
||||
)
|
||||
|
||||
external_plugins.append(plugin_data)
|
||||
external_plugins_ids.append(plugin_data["id"])
|
||||
|
||||
lock = Lock()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,36 @@
|
|||
{% if IS_LOADING != "yes" and DISABLE_DEFAULT_SERVER == "no" +%}
|
||||
root /usr/share/bunkerweb/core/misc/files;
|
||||
location / {
|
||||
try_files /default.html =404;
|
||||
etag off;
|
||||
add_header Last-Modified "";
|
||||
server_tokens off;
|
||||
default_type 'text/html';
|
||||
root /usr/share/bunkerweb/core/misc/files;
|
||||
content_by_lua_block {
|
||||
local utils = require "bunkerweb.utils"
|
||||
local rand = utils.rand
|
||||
local subsystem = ngx.config.subsystem
|
||||
|
||||
local template
|
||||
local render = nil
|
||||
if subsystem == "http" then
|
||||
template = require "resty.template"
|
||||
render = template.render
|
||||
end
|
||||
|
||||
local nonce_style = rand(16)
|
||||
|
||||
-- Override CSP header
|
||||
ngx.header["Content-Security-Policy"] = "default-src 'none'; form-action 'self'; img-src 'self' data:; style-src 'self' 'nonce-"
|
||||
.. nonce_style
|
||||
.. "'; font-src 'self' data:; base-uri 'self'; require-trusted-types-for 'script';"
|
||||
|
||||
-- Remove server header
|
||||
ngx.header["Server"] = nil
|
||||
|
||||
-- Render template
|
||||
render("default.html", {
|
||||
nonce_style = nonce_style,
|
||||
})
|
||||
}
|
||||
}
|
||||
{% endif %}
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,3 +1,4 @@
|
|||
{% set os_path = import("os.path") %}
|
||||
# process rules with disruptive actions
|
||||
SecRuleEngine {{ MODSECURITY_SEC_RULE_ENGINE }}
|
||||
|
||||
|
|
@ -66,7 +67,16 @@ SecAuditLog /var/log/bunkerweb/modsec_audit.log
|
|||
|
||||
# include OWASP CRS configurations
|
||||
{% if USE_MODSECURITY_CRS == "yes" %}
|
||||
{% if MODSECURITY_CRS_VERSION == "nightly" %}
|
||||
{% if os_path.isfile("/var/cache/bunkerweb/modsecurity/crs/crs-setup-nightly.conf") %}
|
||||
include /var/cache/bunkerweb/modsecurity/crs/crs-setup-nightly.conf
|
||||
{% else %}
|
||||
# fallback to the default CRS setup as the nightly one is not available
|
||||
include /usr/share/bunkerweb/core/modsecurity/files/crs-setup-v3.conf
|
||||
{% endif %}
|
||||
{% else %}
|
||||
include /usr/share/bunkerweb/core/modsecurity/files/crs-setup-v{{ MODSECURITY_CRS_VERSION }}.conf
|
||||
{% endif %}
|
||||
|
||||
# custom CRS configurations before loading rules (e.g. exclusions)
|
||||
{% if is_custom_conf("/etc/bunkerweb/configs/modsec-crs") %}
|
||||
|
|
@ -100,7 +110,16 @@ SecRule ENV:is_whitelisted "yes" "id:1000,phase:1,allow,nolog,ctl:ruleEngine=Off
|
|||
{% endif +%}
|
||||
|
||||
# include OWASP CRS rules
|
||||
{% if MODSECURITY_CRS_VERSION == "nightly" %}
|
||||
{% if os_path.exists("/var/cache/bunkerweb/modsecurity/crs/crs-nightly/rules") %}
|
||||
include /var/cache/bunkerweb/modsecurity/crs/crs-nightly/rules/*.conf
|
||||
{% else %}
|
||||
# fallback to the default CRS setup as the nightly one is not available
|
||||
include /usr/share/bunkerweb/core/modsecurity/files/coreruleset-v3/rules/*.conf
|
||||
{% endif %}
|
||||
{% else %}
|
||||
include /usr/share/bunkerweb/core/modsecurity/files/coreruleset-v{{ MODSECURITY_CRS_VERSION }}/rules/*.conf
|
||||
{% endif %}
|
||||
{% endif +%}
|
||||
|
||||
# custom rules after loading the CRS
|
||||
|
|
|
|||
127
src/common/core/modsecurity/jobs/coreruleset-nightly.py
Normal file
127
src/common/core/modsecurity/jobs/coreruleset-nightly.py
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from io import BytesIO
|
||||
from os import getenv, sep
|
||||
from os.path import join
|
||||
from pathlib import Path
|
||||
from re import MULTILINE, search
|
||||
from shutil import rmtree
|
||||
from sys import exit as sys_exit, path as sys_path
|
||||
from tarfile import open as tar_open
|
||||
from threading import Lock
|
||||
from traceback import format_exc
|
||||
|
||||
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("db",))]:
|
||||
if deps_path not in sys_path:
|
||||
sys_path.append(deps_path)
|
||||
|
||||
from requests import RequestException, get
|
||||
|
||||
from logger import setup_logger # type: ignore
|
||||
from jobs import Job # type: ignore
|
||||
|
||||
LOGGER = setup_logger("MODSECURITY.coreruleset-nightly", getenv("LOG_LEVEL", "INFO"))
|
||||
status = 0
|
||||
LOCK = Lock()
|
||||
|
||||
CRS_PATH = Path(sep, "var", "cache", "bunkerweb", "modsecurity", "crs")
|
||||
|
||||
try:
|
||||
# * Check if we're using the nightly version of the Core Rule Set (CRS)
|
||||
use_nightly_crs = False
|
||||
|
||||
if getenv("MODSECURITY_CRS_VERSION", "3") == "nightly":
|
||||
use_nightly_crs = True
|
||||
elif getenv("MULTISITE", "no") == "yes":
|
||||
for first_server in getenv("SERVER_NAME", "").split(" "):
|
||||
if first_server and getenv(f"{first_server}_MODSECURITY_CRS_VERSION", getenv("MODSECURITY_CRS_VERSION", "3")) == "nightly":
|
||||
use_nightly_crs = True
|
||||
break
|
||||
|
||||
if not use_nightly_crs:
|
||||
LOGGER.info("Core Rule Set (CRS) nightly is not being used, skipping download...")
|
||||
sys_exit(0)
|
||||
|
||||
JOB = Job(LOGGER)
|
||||
|
||||
LOGGER.info("Checking if Core Rule Set (CRS) nightly needs to be downloaded...")
|
||||
|
||||
commit_hash = JOB.get_cache("commit_hash")
|
||||
|
||||
resp = get("https://github.com/coreruleset/coreruleset/releases/tag/nightly", timeout=5)
|
||||
resp.raise_for_status()
|
||||
|
||||
content = resp.text
|
||||
|
||||
page_commit_hash = search(r"/coreruleset/coreruleset/commit/(?P<hash>[0-9a-f]{40})", content, MULTILINE)
|
||||
|
||||
if page_commit_hash is None:
|
||||
LOGGER.error("Failed to find commit hash on Core Rule Set (CRS) nightly page.")
|
||||
sys_exit(2)
|
||||
|
||||
page_commit_hash = page_commit_hash.group("hash")
|
||||
|
||||
LOGGER.debug(f"Page commit hash: {page_commit_hash}")
|
||||
|
||||
if commit_hash:
|
||||
LOGGER.debug(f"Current commit hash: {commit_hash.decode()}")
|
||||
|
||||
if commit_hash.decode() == page_commit_hash:
|
||||
LOGGER.info("Core Rule Set (CRS) nightly is up to date.")
|
||||
sys_exit(0)
|
||||
|
||||
LOGGER.info("Core Rule Set (CRS) nightly is outdated.")
|
||||
|
||||
cached, err = JOB.cache_file("commit_hash", page_commit_hash.encode())
|
||||
if not cached:
|
||||
LOGGER.error(f"Failed to cache the Core Rule Set (CRS) nightly commit hash: {err}")
|
||||
status = 2
|
||||
|
||||
LOGGER.info("Downloading Core Rule Set (CRS) nightly tarball...")
|
||||
|
||||
file_content = BytesIO()
|
||||
try:
|
||||
with get("https://github.com/coreruleset/coreruleset/archive/refs/tags/nightly.tar.gz", stream=True, timeout=5) as resp:
|
||||
resp.raise_for_status()
|
||||
for chunk in resp.iter_content(chunk_size=4 * 1024):
|
||||
if chunk:
|
||||
file_content.write(chunk)
|
||||
except RequestException:
|
||||
LOGGER.exception("Failed to download Core Rule Set (CRS) nightly tarball.")
|
||||
sys_exit(2)
|
||||
|
||||
file_content.seek(0)
|
||||
|
||||
rmtree(CRS_PATH, ignore_errors=True)
|
||||
CRS_PATH.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
LOGGER.info("Extracting Core Rule Set (CRS) nightly tarball...")
|
||||
|
||||
with tar_open(fileobj=file_content, mode="r:gz") as tar_file:
|
||||
try:
|
||||
tar_file.extractall(CRS_PATH, filter="data")
|
||||
except TypeError:
|
||||
tar_file.extractall(CRS_PATH)
|
||||
|
||||
# * Rename the extracted folder to "crs-nightly"
|
||||
extracted_folder = next(CRS_PATH.iterdir())
|
||||
extracted_folder.rename(CRS_PATH.joinpath("crs-nightly"))
|
||||
|
||||
# * Move and rename the example configuration file to "crs-setup-nightly.conf"
|
||||
example_conf = CRS_PATH.joinpath("crs-nightly", "crs-setup.conf.example")
|
||||
example_conf.rename(CRS_PATH.joinpath("crs-setup-nightly.conf"))
|
||||
|
||||
cached, err = JOB.cache_dir(CRS_PATH)
|
||||
if not cached:
|
||||
LOGGER.error(f"Error while saving Core Rule Set (CRS) nightly data to db cache: {err}")
|
||||
else:
|
||||
LOGGER.info("Successfully saved Core Rule Set (CRS) nightly data to db cache.")
|
||||
|
||||
status = 1
|
||||
except SystemExit as e:
|
||||
status = e.code
|
||||
except:
|
||||
status = 2
|
||||
LOGGER.error(f"Exception while running coreruleset-nightly.py :\n{format_exc()}")
|
||||
|
||||
sys_exit(status)
|
||||
|
|
@ -26,12 +26,12 @@
|
|||
"MODSECURITY_CRS_VERSION": {
|
||||
"context": "multisite",
|
||||
"default": "3",
|
||||
"help": "Version of the OWASP Core Rule Set to use.",
|
||||
"help": "Version of the OWASP Core Rule Set to use with ModSecurity (3, 4 or nightly).",
|
||||
"id": "modsecurity-crs-version",
|
||||
"label": "Core Rule Set Version",
|
||||
"regex": "^(3|4)$",
|
||||
"regex": "^(3|4|nightly)$",
|
||||
"type": "select",
|
||||
"select": ["3", "4"]
|
||||
"select": ["3", "4", "nightly"]
|
||||
},
|
||||
"MODSECURITY_SEC_AUDIT_ENGINE": {
|
||||
"context": "multisite",
|
||||
|
|
@ -62,5 +62,13 @@
|
|||
"regex": "^A(([B-K])(?!.*\\2))+Z$",
|
||||
"type": "text"
|
||||
}
|
||||
}
|
||||
},
|
||||
"jobs": [
|
||||
{
|
||||
"name": "coreruleset-nightly",
|
||||
"file": "coreruleset-nightly.py",
|
||||
"every": "day",
|
||||
"reload": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ from stat import S_IEXEC
|
|||
from sys import exit as sys_exit, path as sys_path
|
||||
from threading import Lock
|
||||
from uuid import uuid4
|
||||
from json import JSONDecodeError, load, loads
|
||||
from json import JSONDecodeError, load as json_load, loads
|
||||
from shutil import copytree, rmtree
|
||||
from tarfile import open as tar_open
|
||||
from traceback import format_exc
|
||||
|
|
@ -152,9 +152,9 @@ try:
|
|||
metadata = resp.json()["data"]
|
||||
LOGGER.debug(f"Got BunkerWeb Pro license metadata: {metadata}")
|
||||
metadata["pro_expire"] = datetime.strptime(metadata["pro_expire"], "%Y-%m-%d") if metadata["pro_expire"] else None
|
||||
if metadata["pro_services"] < int(data["service_number"]):
|
||||
metadata["pro_overlapped"] = True
|
||||
metadata["is_pro"] = metadata["pro_status"] == "active"
|
||||
if metadata["is_pro"] and metadata["pro_services"] < int(data["service_number"]):
|
||||
metadata["pro_overlapped"] = True
|
||||
|
||||
# ? If we already checked today, skip the check and if the metadata is the same, skip the check
|
||||
if (
|
||||
|
|
@ -188,7 +188,7 @@ try:
|
|||
for chunk in resp.iter_content(chunk_size=8192):
|
||||
resp_content.write(chunk)
|
||||
resp_content.seek(0)
|
||||
resp_data = load(resp_content)
|
||||
resp_data = json_load(resp_content)
|
||||
|
||||
clean = resp_data.get("action") == "clean"
|
||||
|
||||
|
|
@ -212,7 +212,7 @@ try:
|
|||
if not metadata["is_pro"]:
|
||||
if metadata["pro_overlapped"]:
|
||||
LOGGER.warning(
|
||||
f"You have exceeded the number of services allowed by your BunkerWeb Pro license: {metadata['pro_services']} (current: {data['service_number']}"
|
||||
f"You have exceeded the number of services allowed by your BunkerWeb Pro license: {metadata['pro_services']} (current: {data['service_number']})"
|
||||
)
|
||||
|
||||
if pro_license_key:
|
||||
|
|
@ -280,26 +280,27 @@ try:
|
|||
rmtree(plugin_path, ignore_errors=True)
|
||||
continue
|
||||
|
||||
plugin_file = loads(plugin_path.joinpath("plugin.json").read_text(encoding="utf-8"))
|
||||
|
||||
with BytesIO() as plugin_content:
|
||||
with tar_open(fileobj=plugin_content, mode="w:gz", compresslevel=9) as tar:
|
||||
tar.add(plugin_path, arcname=plugin_path.name)
|
||||
plugin_content.seek(0)
|
||||
value = plugin_content.getvalue()
|
||||
tar.add(plugin_path, arcname=plugin_path.name, recursive=True)
|
||||
plugin_content.seek(0, 0)
|
||||
|
||||
plugin_file.update(
|
||||
{
|
||||
"type": "pro",
|
||||
"page": plugin_path.joinpath("ui").is_dir(),
|
||||
"method": "scheduler",
|
||||
"data": value,
|
||||
"checksum": bytes_hash(value, algorithm="sha256"),
|
||||
}
|
||||
)
|
||||
with plugin_path.joinpath("plugin.json").open("r", encoding="utf-8") as f:
|
||||
plugin_data = json_load(f)
|
||||
|
||||
pro_plugins.append(plugin_file)
|
||||
pro_plugins_ids.append(plugin_file["id"])
|
||||
checksum = bytes_hash(plugin_content, algorithm="sha256")
|
||||
plugin_data.update(
|
||||
{
|
||||
"type": "pro",
|
||||
"page": plugin_path.joinpath("ui").is_dir(),
|
||||
"method": "scheduler",
|
||||
"data": plugin_content.getvalue(),
|
||||
"checksum": checksum,
|
||||
}
|
||||
)
|
||||
|
||||
pro_plugins.append(plugin_data)
|
||||
pro_plugins_ids.append(plugin_data["id"])
|
||||
|
||||
lock = Lock()
|
||||
|
||||
|
|
|
|||
|
|
@ -68,9 +68,13 @@ def set_sqlite_pragma(dbapi_connection, _):
|
|||
class Database:
|
||||
DB_STRING_RX = re_compile(r"^(?P<database>(mariadb|mysql)(\+pymysql)?|sqlite(\+pysqlite)?|postgresql(\+psycopg)?):/+(?P<path>/[^\s]+)")
|
||||
|
||||
def __init__(self, logger: Logger, sqlalchemy_string: Optional[str] = None, *, ui: bool = False, pool: Optional[bool] = None, log: bool = True) -> None:
|
||||
def __init__(
|
||||
self, logger: Logger, sqlalchemy_string: Optional[str] = None, *, ui: bool = False, pool: Optional[bool] = None, log: bool = True, **kwargs
|
||||
) -> None:
|
||||
"""Initialize the database"""
|
||||
self.logger = logger
|
||||
self.readonly = False
|
||||
self.last_fallback = None
|
||||
|
||||
if pool:
|
||||
self.logger.warning("The pool parameter is deprecated, it will be removed in the next version")
|
||||
|
|
@ -81,6 +85,16 @@ class Database:
|
|||
if not sqlalchemy_string:
|
||||
sqlalchemy_string = getenv("DATABASE_URI", "sqlite:////var/lib/bunkerweb/db.sqlite3")
|
||||
|
||||
sqlalchemy_string_readonly = getenv("DATABASE_URI_READONLY", "")
|
||||
|
||||
if not sqlalchemy_string:
|
||||
sqlalchemy_string = sqlalchemy_string_readonly or "sqlite:////var/lib/bunkerweb/db.sqlite3"
|
||||
|
||||
if sqlalchemy_string == sqlalchemy_string_readonly:
|
||||
self.readonly = True
|
||||
if log:
|
||||
self.logger.warning("The database connection is set to read-only, the changes will not be saved")
|
||||
|
||||
match = self.DB_STRING_RX.search(sqlalchemy_string)
|
||||
if not match:
|
||||
self.logger.error(f"Invalid database string provided: {sqlalchemy_string}, exiting...")
|
||||
|
|
@ -105,19 +119,20 @@ class Database:
|
|||
) # ? This is strongly recommended as psycopg is the new way to connect to postgresql
|
||||
|
||||
self.database_uri = sqlalchemy_string
|
||||
self.database_uri_readonly = sqlalchemy_string_readonly
|
||||
error = False
|
||||
|
||||
engine_kwargs = {
|
||||
self._engine_kwargs = {
|
||||
"future": True,
|
||||
"poolclass": QueuePool,
|
||||
"pool_pre_ping": True,
|
||||
"pool_recycle": 1800,
|
||||
"pool_size": 40,
|
||||
"max_overflow": 20,
|
||||
}
|
||||
} | kwargs
|
||||
|
||||
try:
|
||||
self.sql_engine = create_engine(sqlalchemy_string, **engine_kwargs)
|
||||
self.sql_engine = create_engine(sqlalchemy_string, **self._engine_kwargs)
|
||||
except ArgumentError:
|
||||
self.logger.error(f"Invalid database URI: {sqlalchemy_string}")
|
||||
error = True
|
||||
|
|
@ -139,29 +154,43 @@ class Database:
|
|||
|
||||
while not_connected:
|
||||
try:
|
||||
with self.sql_engine.connect() as conn:
|
||||
conn.execute(text("CREATE TABLE IF NOT EXISTS test (id INT)"))
|
||||
conn.execute(text("DROP TABLE test"))
|
||||
if self.readonly:
|
||||
with self.sql_engine.connect() as conn:
|
||||
conn.execute(text("SELECT 1"))
|
||||
else:
|
||||
with self.sql_engine.connect() as conn:
|
||||
conn.execute(text("CREATE TABLE IF NOT EXISTS test (id INT)"))
|
||||
conn.execute(text("DROP TABLE test"))
|
||||
|
||||
not_connected = False
|
||||
except (OperationalError, DatabaseError) as e:
|
||||
if retries <= 0:
|
||||
self.logger.error(
|
||||
f"Can't connect to database : {format_exc()}",
|
||||
)
|
||||
_exit(1)
|
||||
if "attempt to write a readonly database" in str(e):
|
||||
if not self.readonly:
|
||||
self.logger.warning("The database is read-only, trying one last time to connect in read-only mode")
|
||||
self.readonly = True
|
||||
self.last_fallback = datetime.now()
|
||||
elif self.database_uri_readonly and sqlalchemy_string != self.database_uri_readonly:
|
||||
self.logger.warning("Can't connect to the database in read-only mode, falling back to read-only one")
|
||||
sqlalchemy_string = self.database_uri_readonly
|
||||
self.last_fallback = datetime.now()
|
||||
else:
|
||||
self.logger.error(f"Can't connect to database : {format_exc()}")
|
||||
_exit(1)
|
||||
else:
|
||||
self.logger.error(f"Can't connect to database : {format_exc()}")
|
||||
_exit(1)
|
||||
|
||||
if "attempt to write a readonly database" in str(e):
|
||||
if log:
|
||||
self.logger.warning("The database is read-only, waiting for it to become writable. Retrying in 5 seconds ...")
|
||||
self.sql_engine.dispose(close=True)
|
||||
self.sql_engine = create_engine(sqlalchemy_string, **engine_kwargs)
|
||||
self.sql_engine = create_engine(sqlalchemy_string, **self._engine_kwargs)
|
||||
if "Unknown table" in str(e):
|
||||
not_connected = False
|
||||
continue
|
||||
elif log:
|
||||
self.logger.warning(
|
||||
"Can't connect to database, retrying in 5 seconds ...",
|
||||
)
|
||||
self.logger.warning("Can't connect to database, retrying in 5 seconds ...")
|
||||
retries -= 1
|
||||
sleep(5)
|
||||
except BaseException:
|
||||
|
|
@ -170,7 +199,7 @@ class Database:
|
|||
|
||||
self.suffix_rx = re_compile(r"_\d+$")
|
||||
if log:
|
||||
self.logger.info("✅ Database connection established")
|
||||
self.logger.info(f"✅ Database connection established{'' if not self.readonly else ' in read-only mode'}")
|
||||
|
||||
def __del__(self) -> None:
|
||||
"""Close the database"""
|
||||
|
|
@ -180,6 +209,26 @@ class Database:
|
|||
if self.sql_engine:
|
||||
self.sql_engine.dispose()
|
||||
|
||||
def retry_connection(self, *, readonly: bool = False, fallback: bool = False, **kwargs) -> None:
|
||||
"""Retry the connection to the database"""
|
||||
|
||||
assert self.sql_engine is not None
|
||||
|
||||
if fallback and not self.database_uri_readonly:
|
||||
raise ValueError("The fallback parameter is set to True but the read-only database URI is not set")
|
||||
|
||||
self.sql_engine.dispose(close=True)
|
||||
self.sql_engine = create_engine(self.database_uri_readonly if fallback else self.database_uri, **self._engine_kwargs | kwargs)
|
||||
|
||||
if fallback or readonly:
|
||||
with self.sql_engine.connect() as conn:
|
||||
conn.execute(text("SELECT 1"))
|
||||
return
|
||||
|
||||
with self.sql_engine.connect() as conn:
|
||||
conn.execute(text("CREATE TABLE IF NOT EXISTS test (id INT)"))
|
||||
conn.execute(text("DROP TABLE test"))
|
||||
|
||||
@contextmanager
|
||||
def __db_session(self) -> Any:
|
||||
try:
|
||||
|
|
@ -188,20 +237,59 @@ class Database:
|
|||
self.logger.error("The database engine is not initialized")
|
||||
_exit(1)
|
||||
|
||||
with self.sql_engine.connect() as conn:
|
||||
session_factory = sessionmaker(bind=conn, autoflush=True, expire_on_commit=False)
|
||||
session = scoped_session(session_factory)
|
||||
if self.database_uri and self.readonly and self.last_fallback and (datetime.now() - self.last_fallback).total_seconds() > 30:
|
||||
# ? If the database is forced to be read-only, we try to connect as a non read-only user every time until the database is writable
|
||||
try:
|
||||
self.retry_connection(pool_timeout=1)
|
||||
self.readonly = False
|
||||
self.logger.info("The database is no longer read-only, defaulting to read-write mode")
|
||||
except (OperationalError, DatabaseError):
|
||||
try:
|
||||
self.retry_connection(readonly=True, pool_timeout=1)
|
||||
except (OperationalError, DatabaseError):
|
||||
if self.database_uri_readonly:
|
||||
with suppress(OperationalError, DatabaseError):
|
||||
self.retry_connection(fallback=True, pool_timeout=1)
|
||||
self.readonly = True
|
||||
|
||||
session = None
|
||||
try:
|
||||
with self.sql_engine.connect() as conn:
|
||||
session_factory = sessionmaker(bind=conn, autoflush=True, expire_on_commit=False)
|
||||
session = scoped_session(session_factory)
|
||||
yield session
|
||||
except BaseException:
|
||||
except BaseException as e:
|
||||
if session:
|
||||
session.rollback()
|
||||
raise
|
||||
finally:
|
||||
|
||||
if "attempt to write a readonly database" in str(e):
|
||||
self.logger.warning("The database is read-only, retrying in read-only mode ...")
|
||||
try:
|
||||
self.retry_connection(readonly=True, pool_timeout=1)
|
||||
except (OperationalError, DatabaseError):
|
||||
if self.database_uri_readonly:
|
||||
self.logger.warning("Can't connect to the database in read-only mode, falling back to read-only one")
|
||||
with suppress(OperationalError, DatabaseError):
|
||||
self.retry_connection(fallback=True, pool_timeout=1)
|
||||
self.readonly = True
|
||||
self.last_fallback = datetime.now()
|
||||
elif isinstance(e, (ConnectionRefusedError, OperationalError)) and self.database_uri_readonly:
|
||||
self.logger.warning("Can't connect to the database, falling back to read-only one ...")
|
||||
with suppress(OperationalError, DatabaseError):
|
||||
self.retry_connection(fallback=True, pool_timeout=1)
|
||||
self.readonly = True
|
||||
self.last_fallback = datetime.now()
|
||||
raise
|
||||
finally:
|
||||
if session:
|
||||
session.remove()
|
||||
|
||||
def set_autoconf_load(self, value: bool = True) -> str:
|
||||
"""Set the autoconf_loaded value"""
|
||||
with self.__db_session() as session:
|
||||
if self.readonly:
|
||||
return "The database is read-only, the changes will not be saved"
|
||||
|
||||
try:
|
||||
metadata = session.query(Metadata).get(1)
|
||||
|
||||
|
|
@ -227,6 +315,9 @@ class Database:
|
|||
def set_scheduler_first_start(self, value: bool = False) -> str:
|
||||
"""Set the scheduler_first_start value"""
|
||||
with self.__db_session() as session:
|
||||
if self.readonly:
|
||||
return "The database is read-only, the changes will not be saved"
|
||||
|
||||
try:
|
||||
metadata = session.query(Metadata).get(1)
|
||||
|
||||
|
|
@ -243,6 +334,9 @@ class Database:
|
|||
def set_pro_metadata(self, data: Dict[Literal["is_pro", "pro_expire", "pro_status", "pro_overlapped", "pro_services"], Any] = {}) -> str:
|
||||
"""Set the pro metadata values"""
|
||||
with self.__db_session() as session:
|
||||
if self.readonly:
|
||||
return "The database is read-only, the changes will not be saved"
|
||||
|
||||
try:
|
||||
metadata = session.query(Metadata).get(1)
|
||||
|
||||
|
|
@ -287,6 +381,9 @@ class Database:
|
|||
def initialize_db(self, version: str, integration: str = "Unknown") -> str:
|
||||
"""Initialize the database"""
|
||||
with self.__db_session() as session:
|
||||
if self.readonly:
|
||||
return "The database is read-only, the changes will not be saved"
|
||||
|
||||
try:
|
||||
metadata = session.query(Metadata).get(1)
|
||||
|
||||
|
|
@ -400,6 +497,9 @@ class Database:
|
|||
"instances",
|
||||
]
|
||||
with self.__db_session() as session:
|
||||
if self.readonly:
|
||||
return "The database is read-only, the changes will not be saved"
|
||||
|
||||
try:
|
||||
metadata = session.query(Metadata).get(1)
|
||||
|
||||
|
|
@ -426,6 +526,10 @@ class Database:
|
|||
|
||||
def init_tables(self, default_plugins: List[dict], bunkerweb_version: str) -> Tuple[bool, str]:
|
||||
"""Initialize the database tables and return the result"""
|
||||
|
||||
if self.readonly:
|
||||
return False, "The database is read-only, the changes will not be saved"
|
||||
|
||||
assert self.sql_engine is not None, "The database engine is not initialized"
|
||||
|
||||
inspector = inspect(self.sql_engine)
|
||||
|
|
@ -846,6 +950,9 @@ class Database:
|
|||
"""Save the config in the database"""
|
||||
to_put = []
|
||||
with self.__db_session() as session:
|
||||
if self.readonly:
|
||||
return "The database is read-only, the changes will not be saved"
|
||||
|
||||
# Delete all the old config
|
||||
session.query(Global_values).filter(Global_values.method == method).delete()
|
||||
session.query(Services_settings).filter(Services_settings.method == method).delete()
|
||||
|
|
@ -1098,6 +1205,9 @@ class Database:
|
|||
"""Save the custom configs in the database"""
|
||||
message = ""
|
||||
with self.__db_session() as session:
|
||||
if self.readonly:
|
||||
return "The database is read-only, the changes will not be saved"
|
||||
|
||||
# Delete all the old config
|
||||
session.query(Custom_configs).filter(Custom_configs.method == method).delete()
|
||||
|
||||
|
|
@ -1303,6 +1413,9 @@ class Database:
|
|||
def update_job(self, plugin_id: str, job_name: str, success: bool) -> str:
|
||||
"""Update the job last_run in the database"""
|
||||
with self.__db_session() as session:
|
||||
if self.readonly:
|
||||
return "The database is read-only, the changes will not be saved"
|
||||
|
||||
job = session.query(Jobs).filter_by(plugin_id=plugin_id, name=job_name).first()
|
||||
|
||||
if not job:
|
||||
|
|
@ -1318,7 +1431,7 @@ class Database:
|
|||
|
||||
return ""
|
||||
|
||||
def delete_job_cache(self, file_name: str, *, job_name: Optional[str] = None, service_id: Optional[str] = None):
|
||||
def delete_job_cache(self, file_name: str, *, job_name: Optional[str] = None, service_id: Optional[str] = None) -> str:
|
||||
job_name = job_name or argv[0].replace(".py", "")
|
||||
filters = {"file_name": file_name}
|
||||
if job_name:
|
||||
|
|
@ -1327,7 +1440,15 @@ class Database:
|
|||
filters["service_id"] = service_id
|
||||
|
||||
with self.__db_session() as session:
|
||||
session.query(Jobs_cache).filter_by(**filters).delete()
|
||||
if self.readonly:
|
||||
return "The database is read-only, the changes will not be saved"
|
||||
|
||||
try:
|
||||
session.query(Jobs_cache).filter_by(**filters).delete()
|
||||
except BaseException:
|
||||
return format_exc()
|
||||
|
||||
return ""
|
||||
|
||||
def upsert_job_cache(
|
||||
self,
|
||||
|
|
@ -1342,6 +1463,9 @@ class Database:
|
|||
job_name = job_name or argv[0].replace(".py", "")
|
||||
service_id = service_id or None
|
||||
with self.__db_session() as session:
|
||||
if self.readonly:
|
||||
return "The database is read-only, the changes will not be saved"
|
||||
|
||||
cache = session.query(Jobs_cache).filter_by(job_name=job_name, service_id=service_id, file_name=file_name).first()
|
||||
|
||||
if not cache:
|
||||
|
|
@ -1372,6 +1496,9 @@ class Database:
|
|||
to_put = []
|
||||
changes = False
|
||||
with self.__db_session() as session:
|
||||
if self.readonly:
|
||||
return "The database is read-only, the changes will not be saved"
|
||||
|
||||
db_plugins = session.query(Plugins).with_entities(Plugins.id).filter_by(type=_type).all()
|
||||
|
||||
db_ids = []
|
||||
|
|
@ -1422,6 +1549,10 @@ class Database:
|
|||
)
|
||||
|
||||
if db_plugin:
|
||||
if plugin["method"] not in (db_plugin.method, "autoconf"):
|
||||
self.logger.warning(f'Plugin "{plugin["id"]}" already exists, but the method is different, skipping update')
|
||||
continue
|
||||
|
||||
if db_plugin.type not in ("external", "pro"):
|
||||
self.logger.warning(
|
||||
f"Plugin \"{plugin['id']}\" is not {_type}, skipping update (updating a non-external or non-pro plugin is forbidden for security reasons)", # noqa: E501
|
||||
|
|
@ -1896,7 +2027,6 @@ class Database:
|
|||
"method": plugin.method,
|
||||
"page": page is not None,
|
||||
"settings": {},
|
||||
"bwcli": {},
|
||||
"checksum": plugin.checksum,
|
||||
} | ({"data": plugin.data} if with_data else {})
|
||||
|
||||
|
|
@ -1932,6 +2062,8 @@ class Database:
|
|||
]
|
||||
|
||||
for command in session.query(BwcliCommands).with_entities(BwcliCommands.name, BwcliCommands.file_name).filter_by(plugin_id=plugin.id):
|
||||
if "bwcli" not in data:
|
||||
data["bwcli"] = {}
|
||||
data["bwcli"][command.name] = command.file_name
|
||||
|
||||
plugins.append(data)
|
||||
|
|
@ -2062,6 +2194,9 @@ class Database:
|
|||
def add_instance(self, hostname: str, port: int, server_name: str, changed: Optional[bool] = True) -> str:
|
||||
"""Add instance."""
|
||||
with self.__db_session() as session:
|
||||
if self.readonly:
|
||||
return "The database is read-only, the changes will not be saved"
|
||||
|
||||
db_instance = session.query(Instances).with_entities(Instances.hostname).filter_by(hostname=hostname).first()
|
||||
|
||||
if db_instance is not None:
|
||||
|
|
@ -2086,6 +2221,9 @@ class Database:
|
|||
"""Update instances."""
|
||||
to_put = []
|
||||
with self.__db_session() as session:
|
||||
if self.readonly:
|
||||
return "The database is read-only, the changes will not be saved"
|
||||
|
||||
session.query(Instances).delete()
|
||||
|
||||
for instance in instances:
|
||||
|
|
@ -2175,7 +2313,11 @@ class Database:
|
|||
def create_ui_user(self, username: str, password: bytes, *, secret_token: Optional[str] = None, method: str = "manual") -> str:
|
||||
"""Create ui user."""
|
||||
with self.__db_session() as session:
|
||||
if self.get_ui_user():
|
||||
if self.readonly:
|
||||
return "The database is read-only, the changes will not be saved"
|
||||
|
||||
user = session.query(Users).filter_by(id=1).first()
|
||||
if user:
|
||||
return "User already exists"
|
||||
|
||||
session.add(Users(id=1, username=username, password=password.decode("utf-8"), secret_token=secret_token, method=method))
|
||||
|
|
@ -2192,6 +2334,9 @@ class Database:
|
|||
) -> str:
|
||||
"""Update ui user."""
|
||||
with self.__db_session() as session:
|
||||
if self.readonly:
|
||||
return "The database is read-only, the changes will not be saved"
|
||||
|
||||
user = session.query(Users).filter_by(id=1).first()
|
||||
if not user:
|
||||
return "User not found"
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
cryptography==42.0.7
|
||||
psycopg[c,pool]==3.1.19
|
||||
PyMySQL==1.1.0
|
||||
PyMySQL==1.1.1
|
||||
sqlalchemy==2.0.30
|
||||
|
|
|
|||
|
|
@ -167,9 +167,9 @@ pycparser==2.22 \
|
|||
--hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \
|
||||
--hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc
|
||||
# via cffi
|
||||
pymysql==1.1.0 \
|
||||
--hash=sha256:4f13a7df8bf36a51e81dd9f3605fede45a4878fe02f9236349fd82a3f0612f96 \
|
||||
--hash=sha256:8969ec6d763c856f7073c4c64662882675702efcb114b4bcbb955aea3a069fa7
|
||||
pymysql==1.1.1 \
|
||||
--hash=sha256:4de15da4c61dc132f4fb9ab763063e693d521a80fd0e87943b9a453dd4c19d6c \
|
||||
--hash=sha256:e127611aaf2b417403c60bf4dc570124aeb4a57f5f37b8e95ae399a42f904cd0
|
||||
# via -r requirements.armv7.in
|
||||
sqlalchemy==2.0.30 \
|
||||
--hash=sha256:0094c5dc698a5f78d3d1539853e8ecec02516b62b8223c970c86d44e7a80f6c7 \
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
cryptography==42.0.7
|
||||
psycopg[binary,pool]==3.1.19
|
||||
PyMySQL==1.1.0
|
||||
PyMySQL==1.1.1
|
||||
sqlalchemy==2.0.30
|
||||
|
|
|
|||
|
|
@ -229,9 +229,9 @@ pycparser==2.22 \
|
|||
--hash=sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6 \
|
||||
--hash=sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc
|
||||
# via cffi
|
||||
pymysql==1.1.0 \
|
||||
--hash=sha256:4f13a7df8bf36a51e81dd9f3605fede45a4878fe02f9236349fd82a3f0612f96 \
|
||||
--hash=sha256:8969ec6d763c856f7073c4c64662882675702efcb114b4bcbb955aea3a069fa7
|
||||
pymysql==1.1.1 \
|
||||
--hash=sha256:4de15da4c61dc132f4fb9ab763063e693d521a80fd0e87943b9a453dd4c19d6c \
|
||||
--hash=sha256:e127611aaf2b417403c60bf4dc570124aeb4a57f5f37b8e95ae399a42f904cd0
|
||||
# via -r requirements.in
|
||||
sqlalchemy==2.0.30 \
|
||||
--hash=sha256:0094c5dc698a5f78d3d1539853e8ecec02516b62b8223c970c86d44e7a80f6c7 \
|
||||
|
|
|
|||
|
|
@ -277,9 +277,9 @@ redis==5.0.4 \
|
|||
--hash=sha256:7adc2835c7a9b5033b7ad8f8918d09b7344188228809c98df07af226d39dec91 \
|
||||
--hash=sha256:ec31f2ed9675cc54c21ba854cfe0462e6faf1d83c8ce5944709db8a4700b9c61
|
||||
# via -r requirements.in
|
||||
requests==2.31.0 \
|
||||
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
|
||||
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
|
||||
requests==2.32.2 \
|
||||
--hash=sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289 \
|
||||
--hash=sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c
|
||||
# via
|
||||
# docker
|
||||
# kubernetes
|
||||
|
|
|
|||
|
|
@ -2,6 +2,6 @@ pip==24.0
|
|||
pip-compile-multi==2.6.3
|
||||
pip-tools==7.4.1
|
||||
pip-upgrader==1.4.15
|
||||
setuptools==69.5.1
|
||||
setuptools==70.0.0
|
||||
tomli==2.0.1
|
||||
wheel==0.43.0
|
||||
|
|
|
|||
|
|
@ -157,15 +157,15 @@ pyproject-hooks==1.1.0 \
|
|||
# via
|
||||
# build
|
||||
# pip-tools
|
||||
requests==2.31.0 \
|
||||
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
|
||||
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
|
||||
requests==2.32.2 \
|
||||
--hash=sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289 \
|
||||
--hash=sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c
|
||||
# via
|
||||
# -r requirements-deps.in
|
||||
# pip-tools
|
||||
setuptools==69.5.1 \
|
||||
--hash=sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987 \
|
||||
--hash=sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32
|
||||
setuptools==70.0.0 \
|
||||
--hash=sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4 \
|
||||
--hash=sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0
|
||||
# via pip-upgrader
|
||||
terminaltables==3.1.10 \
|
||||
--hash=sha256:ba6eca5cb5ba02bba4c9f4f985af80c54ec3dccf94cfcd190154386255e47543 \
|
||||
|
|
@ -192,9 +192,9 @@ wheel==0.43.0 \
|
|||
# via
|
||||
# -r requirements-deps.in
|
||||
# pip-tools
|
||||
zipp==3.18.1 \
|
||||
--hash=sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b \
|
||||
--hash=sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715
|
||||
zipp==3.18.2 \
|
||||
--hash=sha256:6278d9ddbcfb1f1089a88fde84481528b07b0e10474e09dcfe53dad4069fa059 \
|
||||
--hash=sha256:dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e
|
||||
# via
|
||||
# -r requirements-deps.in
|
||||
# pip-tools
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
pip==24.0
|
||||
pip-tools==7.4.1
|
||||
setuptools==69.5.1
|
||||
setuptools==70.0.0
|
||||
wheel==0.43.0
|
||||
|
|
|
|||
|
|
@ -36,9 +36,9 @@ pyproject-hooks==1.1.0 \
|
|||
# via
|
||||
# -r requirements.in
|
||||
# pip-tools
|
||||
setuptools==69.5.1 \
|
||||
--hash=sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987 \
|
||||
--hash=sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32
|
||||
setuptools==70.0.0 \
|
||||
--hash=sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4 \
|
||||
--hash=sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0
|
||||
# via
|
||||
# build
|
||||
# pip-tools
|
||||
|
|
@ -54,9 +54,9 @@ wheel==0.43.0 \
|
|||
# via
|
||||
# -r requirements.in
|
||||
# pip-tools
|
||||
zipp==3.18.1 \
|
||||
--hash=sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b \
|
||||
--hash=sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715
|
||||
zipp==3.18.2 \
|
||||
--hash=sha256:6278d9ddbcfb1f1089a88fde84481528b07b0e10474e09dcfe53dad4069fa059 \
|
||||
--hash=sha256:dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e
|
||||
# via
|
||||
# -r requirements.in
|
||||
# pip-tools
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ WORKDIR /usr/share/bunkerweb
|
|||
|
||||
# Install fpm
|
||||
RUN dnf update -y && \
|
||||
dnf install -y ruby ruby-devel redhat-rpm-config rpm-build && \
|
||||
dnf install -y ruby ruby-devel redhat-rpm-config rpm-build gcc make && \
|
||||
gem install -N fpm
|
||||
|
||||
# Setup BW
|
||||
|
|
|
|||
|
|
@ -90,10 +90,11 @@ COPY src/linux/RPM-GPG-KEY-centosofficial /etc/pki/rpm-gpg/RPM-GPG-KEY-centosoff
|
|||
RUN rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial
|
||||
|
||||
# Install fpm
|
||||
RUN dnf install -y wget redhat-rpm-config rpm-build yum-utils && \
|
||||
RUN dnf install -y wget redhat-rpm-config rpm-build yum-utils gcc make && \
|
||||
wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm && \
|
||||
rpm -Uvh epel-release*rpm && \
|
||||
dnf module -y reset ruby && dnf module -y enable ruby:3.1 && dnf module -y install ruby:3.1/common && \
|
||||
dnf install -y ruby-devel && \
|
||||
gem install fpm
|
||||
|
||||
# Setup BW
|
||||
|
|
|
|||
|
|
@ -81,10 +81,11 @@ COPY --from=builder --chown=0:101 /usr/share/bunkerweb /usr/share/bunkerweb
|
|||
WORKDIR /usr/share/bunkerweb
|
||||
|
||||
# Install fpm
|
||||
RUN dnf install -y wget redhat-rpm-config rpm-build yum-utils && \
|
||||
RUN dnf install -y wget redhat-rpm-config rpm-build yum-utils gcc make && \
|
||||
wget https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm && \
|
||||
rpm -Uvh epel-release*rpm && \
|
||||
dnf module -y reset ruby && dnf module -y enable ruby:3.1 && dnf module -y install ruby:3.1/common && \
|
||||
dnf install -y ruby-devel && \
|
||||
gem install fpm
|
||||
|
||||
# Setup BW
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ COPY --chown=root:scheduler --chmod=770 src/bw/misc/asn.mmdb /var/tmp/bunkerweb/
|
|||
COPY --chown=root:scheduler --chmod=770 src/bw/misc/country.mmdb /var/tmp/bunkerweb/country.mmdb
|
||||
|
||||
# Fix CVEs
|
||||
# There are no CVEs to fix for this image
|
||||
RUN apk add --no-cache "busybox>=1.36.1-r17" "busybox-binsh>=1.36.1-r17" "ssl_client>=1.36.1-r17" # CVE-2023-42363 CVE-2023-42364 CVE-2023-42365 CVE-2023-42366
|
||||
|
||||
LABEL maintainer "Bunkerity <contact@bunkerity.com>"
|
||||
LABEL version "1.5.7"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from contextlib import suppress
|
||||
from copy import deepcopy
|
||||
from functools import partial
|
||||
from glob import glob
|
||||
|
|
@ -45,7 +46,7 @@ class JobScheduler(ApiCaller):
|
|||
super().__init__(apis or [])
|
||||
self.__logger = logger or setup_logger("Scheduler", getenv("LOG_LEVEL", "INFO"))
|
||||
self.__integration = integration
|
||||
self.__db = db or Database(self.__logger)
|
||||
self.db = db or Database(self.__logger)
|
||||
self.__env = env or {}
|
||||
self.__env.update(environ)
|
||||
self.__jobs = self.__get_jobs()
|
||||
|
|
@ -76,7 +77,7 @@ class JobScheduler(ApiCaller):
|
|||
apis = []
|
||||
try:
|
||||
with self.__thread_lock:
|
||||
instances = self.__db.get_instances()
|
||||
instances = self.db.get_instances()
|
||||
for instance in instances:
|
||||
api = API(f"http://{instance['hostname']}:{instance['port']}", host=instance["server_name"])
|
||||
apis.append(api)
|
||||
|
|
@ -204,7 +205,7 @@ class JobScheduler(ApiCaller):
|
|||
|
||||
def __update_job(self, plugin: str, name: str, success: bool):
|
||||
with self.__thread_lock:
|
||||
err = self.__db.update_job(plugin, name, success)
|
||||
err = self.db.update_job(plugin, name, success)
|
||||
|
||||
if not err:
|
||||
self.__logger.info(f"Successfully updated database for the job {name} from plugin {plugin}")
|
||||
|
|
@ -226,14 +227,35 @@ class JobScheduler(ApiCaller):
|
|||
|
||||
def run_pending(self) -> bool:
|
||||
threads = []
|
||||
self.__job_success = True
|
||||
self.__job_reload = False
|
||||
|
||||
for job in schedule_jobs:
|
||||
if not job.should_run:
|
||||
continue
|
||||
threads.append(Thread(target=self.__run_in_thread, args=((job.run,),)))
|
||||
|
||||
if not threads:
|
||||
return True
|
||||
|
||||
if self.db.database_uri and self.db.readonly:
|
||||
try:
|
||||
self.db.retry_connection(pool_timeout=5)
|
||||
self.db.readonly = False
|
||||
self.__logger.info("The database is no longer read-only, defaulting to read-write mode")
|
||||
except BaseException:
|
||||
try:
|
||||
self.db.retry_connection(readonly=True, pool_timeout=5)
|
||||
except BaseException:
|
||||
if self.db.database_uri_readonly:
|
||||
with suppress(BaseException):
|
||||
self.db.retry_connection(fallback=True, pool_timeout=5)
|
||||
self.db.readonly = True
|
||||
|
||||
if self.db.readonly:
|
||||
self.__logger.error("Database is in read-only mode, jobs will not be executed")
|
||||
return True
|
||||
|
||||
self.__job_success = True
|
||||
self.__job_reload = False
|
||||
|
||||
for thread in threads:
|
||||
thread.start()
|
||||
|
||||
|
|
@ -267,6 +289,24 @@ class JobScheduler(ApiCaller):
|
|||
return success
|
||||
|
||||
def run_once(self) -> bool:
|
||||
if self.db.database_uri and self.db.readonly:
|
||||
try:
|
||||
self.db.retry_connection(pool_timeout=1)
|
||||
self.db.readonly = False
|
||||
self.__logger.info("The database is no longer read-only, defaulting to read-write mode")
|
||||
except BaseException:
|
||||
try:
|
||||
self.db.retry_connection(readonly=True, pool_timeout=1)
|
||||
except BaseException:
|
||||
if self.db.database_uri_readonly:
|
||||
with suppress(BaseException):
|
||||
self.db.retry_connection(fallback=True, pool_timeout=1)
|
||||
self.db.readonly = True
|
||||
|
||||
if self.db.readonly:
|
||||
self.__logger.error("Database is in read-only mode, jobs will not be executed")
|
||||
return True
|
||||
|
||||
threads = []
|
||||
self.__job_success = True
|
||||
self.__job_reload = False
|
||||
|
|
@ -290,6 +330,24 @@ class JobScheduler(ApiCaller):
|
|||
return ret
|
||||
|
||||
def run_single(self, job_name: str) -> bool:
|
||||
if self.db.database_uri and self.db.readonly:
|
||||
try:
|
||||
self.db.retry_connection(pool_timeout=1)
|
||||
self.db.readonly = False
|
||||
self.__logger.info("The database is no longer read-only, defaulting to read-write mode")
|
||||
except BaseException:
|
||||
try:
|
||||
self.db.retry_connection(readonly=True, pool_timeout=1)
|
||||
except BaseException:
|
||||
if self.db.database_uri_readonly:
|
||||
with suppress(BaseException):
|
||||
self.db.retry_connection(fallback=True, pool_timeout=1)
|
||||
self.db.readonly = True
|
||||
|
||||
if self.db.readonly:
|
||||
self.__logger.error("Database is in read-only mode, jobs will not be executed")
|
||||
return True
|
||||
|
||||
if self.__lock:
|
||||
self.__lock.acquire()
|
||||
|
||||
|
|
|
|||
|
|
@ -173,9 +173,22 @@ def generate_external_plugins(plugins: List[Dict[str, Any]], *, original_path: U
|
|||
pro = "pro" in original_path.parts
|
||||
|
||||
# Remove old external/pro plugins files
|
||||
logger.info(f"Removing old {'pro ' if pro else ''}external plugins files ...")
|
||||
logger.info(f"Removing old/changed {'pro ' if pro else ''}external plugins files ...")
|
||||
ignored_plugins = set()
|
||||
if original_path.is_dir():
|
||||
for file in original_path.glob("*"):
|
||||
with suppress(StopIteration, IndexError):
|
||||
index = next(i for i, plugin in enumerate(plugins) if plugin["id"] == file.name)
|
||||
|
||||
with BytesIO() as plugin_content:
|
||||
with tar_open(fileobj=plugin_content, mode="w:gz", compresslevel=9) as tar:
|
||||
tar.add(file, arcname=file.name, recursive=True)
|
||||
plugin_content.seek(0, 0)
|
||||
if bytes_hash(plugin_content, algorithm="sha256") == plugins[index]["checksum"]:
|
||||
ignored_plugins.add(file.name)
|
||||
continue
|
||||
logger.debug(f"Checksum of {file} has changed, removing it ...")
|
||||
|
||||
if file.is_symlink() or file.is_file():
|
||||
with suppress(OSError):
|
||||
file.unlink()
|
||||
|
|
@ -186,6 +199,9 @@ def generate_external_plugins(plugins: List[Dict[str, Any]], *, original_path: U
|
|||
logger.info(f"Generating new {'pro ' if pro else ''}external plugins ...")
|
||||
original_path.mkdir(parents=True, exist_ok=True)
|
||||
for plugin in plugins:
|
||||
if plugin["id"] in ignored_plugins:
|
||||
continue
|
||||
|
||||
try:
|
||||
if plugin["data"]:
|
||||
tmp_path = TMP_PATH.joinpath(f"{plugin['id']}_{plugin['name']}.tar.gz")
|
||||
|
|
@ -272,33 +288,32 @@ def api_to_instance(api):
|
|||
}
|
||||
|
||||
|
||||
def run_in_slave_mode(db: Database, dotenv_env: Dict[str, Any]):
|
||||
# Instantiate db
|
||||
db = Database(logger, sqlalchemy_string=dotenv_env.get("DATABASE_URI", getenv("DATABASE_URI", None)))
|
||||
def run_in_slave_mode():
|
||||
assert SCHEDULER is not None
|
||||
|
||||
# Wait for init
|
||||
while not db.is_initialized():
|
||||
while not SCHEDULER.db.is_initialized():
|
||||
logger.warning("Database is not initialized, retrying in 5s ...")
|
||||
sleep(5)
|
||||
|
||||
# Wait for first config
|
||||
env = db.get_config()
|
||||
while not db.is_first_config_saved() or not env:
|
||||
env = SCHEDULER.db.get_config()
|
||||
while not SCHEDULER.db.is_first_config_saved() or not env:
|
||||
logger.warning("Database doesn't have any config saved yet, retrying in 5s ...")
|
||||
sleep(5)
|
||||
env = db.get_config()
|
||||
env = SCHEDULER.db.get_config()
|
||||
|
||||
# Download plugins
|
||||
pro_plugins = db.get_plugins(_type="pro", with_data=True)
|
||||
pro_plugins = SCHEDULER.db.get_plugins(_type="pro", with_data=True)
|
||||
generate_external_plugins(pro_plugins, original_path=PRO_PLUGINS_PATH)
|
||||
external_plugins = db.get_plugins(_type="external", with_data=True)
|
||||
external_plugins = SCHEDULER.db.get_plugins(_type="external", with_data=True)
|
||||
generate_external_plugins(external_plugins)
|
||||
|
||||
# Download custom configs
|
||||
generate_custom_configs(db.get_custom_configs())
|
||||
generate_custom_configs(SCHEDULER.db.get_custom_configs())
|
||||
|
||||
# Download caches
|
||||
generate_caches(pro_plugins + external_plugins, db)
|
||||
generate_caches(pro_plugins + external_plugins, SCHEDULER.db)
|
||||
|
||||
# Gen config
|
||||
content = ""
|
||||
|
|
@ -353,18 +368,18 @@ if __name__ == "__main__":
|
|||
nginx_variables_path = Path(sep, "etc", "nginx", "variables.env")
|
||||
dotenv_env = dotenv_values(str(tmp_variables_path))
|
||||
|
||||
db = Database(logger, sqlalchemy_string=dotenv_env.get("DATABASE_URI", getenv("DATABASE_URI", None)))
|
||||
SCHEDULER = JobScheduler(environ, logger, INTEGRATION, db=Database(logger, sqlalchemy_string=dotenv_env.get("DATABASE_URI", getenv("DATABASE_URI", None)))) # type: ignore
|
||||
|
||||
if SLAVE_MODE:
|
||||
run_in_slave_mode(db, dotenv_env)
|
||||
run_in_slave_mode()
|
||||
stop(1)
|
||||
|
||||
if INTEGRATION in ("Swarm", "Kubernetes", "Autoconf"):
|
||||
while not db.is_initialized():
|
||||
while not SCHEDULER.db.is_initialized():
|
||||
logger.warning("Database is not initialized, retrying in 5s ...")
|
||||
sleep(5)
|
||||
|
||||
while not db.is_autoconf_loaded():
|
||||
while not SCHEDULER.db.is_autoconf_loaded():
|
||||
logger.warning("Autoconf is not loaded yet in the database, retrying in 5s ...")
|
||||
sleep(5)
|
||||
|
||||
|
|
@ -373,8 +388,8 @@ if __name__ == "__main__":
|
|||
or not tmp_variables_path.exists()
|
||||
or not nginx_variables_path.exists()
|
||||
or (tmp_variables_path.read_text(encoding="utf-8") != nginx_variables_path.read_text(encoding="utf-8"))
|
||||
or db.is_initialized()
|
||||
and db.get_config() != dotenv_env
|
||||
or SCHEDULER.db.is_initialized()
|
||||
and SCHEDULER.db.get_config() != dotenv_env
|
||||
):
|
||||
# run the config saver
|
||||
proc = subprocess_run(
|
||||
|
|
@ -393,19 +408,19 @@ if __name__ == "__main__":
|
|||
logger.error("Config saver failed, configuration will not work as expected...")
|
||||
|
||||
if INTEGRATION not in ("Swarm", "Kubernetes", "Autoconf"):
|
||||
while not db.is_initialized():
|
||||
while not SCHEDULER.db.is_initialized():
|
||||
logger.warning("Database is not initialized, retrying in 5s ...")
|
||||
sleep(5)
|
||||
|
||||
env = db.get_config()
|
||||
while not db.is_first_config_saved() or not env:
|
||||
env = SCHEDULER.db.get_config()
|
||||
while not SCHEDULER.db.is_first_config_saved() or not env:
|
||||
logger.warning("Database doesn't have any config saved yet, retrying in 5s ...")
|
||||
sleep(5)
|
||||
env = db.get_config()
|
||||
env = SCHEDULER.db.get_config()
|
||||
|
||||
env = db.get_config()
|
||||
env = SCHEDULER.db.get_config()
|
||||
|
||||
env["DATABASE_URI"] = db.database_uri
|
||||
env["DATABASE_URI"] = SCHEDULER.db.database_uri
|
||||
|
||||
# Override instances if needed
|
||||
override_instances = env.get("OVERRIDE_INSTANCES", "")
|
||||
|
|
@ -415,7 +430,7 @@ if __name__ == "__main__":
|
|||
apis.append(API(instance))
|
||||
|
||||
# Instantiate scheduler
|
||||
SCHEDULER = JobScheduler(env | environ, logger, INTEGRATION, db=db, apis=apis)
|
||||
SCHEDULER.env = env | environ
|
||||
|
||||
if INTEGRATION in ("Docker", "Swarm", "Kubernetes", "Autoconf"):
|
||||
# Automatically setup the scheduler apis
|
||||
|
|
@ -425,16 +440,16 @@ if __name__ == "__main__":
|
|||
if not SCHEDULER.apis:
|
||||
logger.warning("No BunkerWeb API found, retrying in 5s ...")
|
||||
sleep(5)
|
||||
db.update_instances([api_to_instance(api) for api in SCHEDULER.apis])
|
||||
SCHEDULER.db.update_instances([api_to_instance(api) for api in SCHEDULER.apis])
|
||||
|
||||
scheduler_first_start = db.is_scheduler_first_start()
|
||||
scheduler_first_start = SCHEDULER.db.is_scheduler_first_start()
|
||||
|
||||
logger.info("Scheduler started ...")
|
||||
|
||||
# Checking if any custom config has been created by the user
|
||||
logger.info("Checking if there are any changes in custom configs ...")
|
||||
custom_configs = []
|
||||
db_configs = db.get_custom_configs()
|
||||
db_configs = SCHEDULER.db.get_custom_configs()
|
||||
changes = False
|
||||
for file in CUSTOM_CONFIGS_PATH.rglob("*.conf"):
|
||||
if len(file.parts) > len(CUSTOM_CONFIGS_PATH.parts) + 3:
|
||||
|
|
@ -460,12 +475,12 @@ if __name__ == "__main__":
|
|||
changes = changes or {hash(dict_to_frozenset(d)) for d in custom_configs} != {hash(dict_to_frozenset(d)) for d in db_configs}
|
||||
|
||||
if changes:
|
||||
err = db.save_custom_configs(custom_configs, "manual")
|
||||
err = SCHEDULER.db.save_custom_configs(custom_configs, "manual")
|
||||
if err:
|
||||
logger.error(f"Couldn't save some manually created custom configs to database: {err}")
|
||||
|
||||
if (scheduler_first_start and db_configs) or changes:
|
||||
generate_custom_configs(db.get_custom_configs())
|
||||
generate_custom_configs(SCHEDULER.db.get_custom_configs())
|
||||
|
||||
del custom_configs, db_configs
|
||||
|
||||
|
|
@ -473,51 +488,53 @@ if __name__ == "__main__":
|
|||
# Check if any external or pro plugin has been added by the user
|
||||
logger.info(f"Checking if there are any changes in {_type} plugins ...")
|
||||
plugin_path = EXTERNAL_PLUGINS_PATH if _type == "external" else PRO_PLUGINS_PATH
|
||||
db_plugins = SCHEDULER.db.get_plugins(_type=_type)
|
||||
external_plugins = []
|
||||
tmp_external_plugins = []
|
||||
for file in plugin_path.glob("*/plugin.json"):
|
||||
plugin_content = BytesIO()
|
||||
with tar_open(fileobj=plugin_content, mode="w:gz", compresslevel=9) as tar:
|
||||
tar.add(file.parent, arcname=file.parent.name, recursive=True)
|
||||
plugin_content.seek(0, 0)
|
||||
with BytesIO() as plugin_content:
|
||||
with tar_open(fileobj=plugin_content, mode="w:gz", compresslevel=9) as tar:
|
||||
tar.add(file.parent, arcname=file.parent.name, recursive=True)
|
||||
plugin_content.seek(0, 0)
|
||||
|
||||
with file.open("r", encoding="utf-8") as f:
|
||||
plugin_data = json_load(f)
|
||||
with file.open("r", encoding="utf-8") as f:
|
||||
plugin_data = json_load(f)
|
||||
|
||||
common_data = plugin_data | {
|
||||
"type": _type,
|
||||
"page": file.parent.joinpath("ui").is_dir(),
|
||||
}
|
||||
jobs = common_data.pop("jobs", [])
|
||||
|
||||
tmp_external_plugins.append(common_data)
|
||||
|
||||
checksum = bytes_hash(plugin_content, algorithm="sha256")
|
||||
external_plugins.append(
|
||||
common_data
|
||||
| {
|
||||
"method": "manual",
|
||||
"data": plugin_content.getvalue(),
|
||||
checksum = bytes_hash(plugin_content, algorithm="sha256")
|
||||
common_data = plugin_data | {
|
||||
"type": _type,
|
||||
"page": file.parent.joinpath("ui").is_dir(),
|
||||
"checksum": checksum,
|
||||
}
|
||||
| ({"jobs": jobs} if jobs else {})
|
||||
)
|
||||
jobs = common_data.pop("jobs", [])
|
||||
|
||||
db_plugins = db.get_plugins(_type=_type)
|
||||
tmp_db_plugins = []
|
||||
for db_plugin in db_plugins.copy():
|
||||
db_plugin.pop("method", None)
|
||||
tmp_db_plugins.append(db_plugin)
|
||||
with suppress(StopIteration, IndexError):
|
||||
index = next(i for i, plugin in enumerate(db_plugins) if plugin["id"] == common_data["id"])
|
||||
|
||||
changes = {hash(dict_to_frozenset(d)) for d in tmp_external_plugins} != {hash(dict_to_frozenset(d)) for d in tmp_db_plugins}
|
||||
if checksum == db_plugins[index]["checksum"] or db_plugins[index]["method"] != "manual":
|
||||
continue
|
||||
|
||||
if changes:
|
||||
err = db.update_external_plugins(external_plugins, _type=_type, delete_missing=True)
|
||||
if err:
|
||||
logger.error(f"Couldn't save some manually added {_type} plugins to database: {err}")
|
||||
tmp_external_plugins.append(common_data.copy())
|
||||
|
||||
if (scheduler_first_start and db_plugins) or changes:
|
||||
generate_external_plugins(db.get_plugins(_type=_type, with_data=True), original_path=plugin_path)
|
||||
external_plugins.append(
|
||||
common_data
|
||||
| {
|
||||
"method": "manual",
|
||||
"data": plugin_content.getvalue(),
|
||||
}
|
||||
| ({"jobs": jobs} if jobs else {})
|
||||
)
|
||||
|
||||
if tmp_external_plugins:
|
||||
changes = {hash(dict_to_frozenset(d)) for d in tmp_external_plugins} != {hash(dict_to_frozenset(d)) for d in db_plugins}
|
||||
|
||||
if changes:
|
||||
err = SCHEDULER.db.update_external_plugins(external_plugins, _type=_type, delete_missing=True)
|
||||
if err:
|
||||
logger.error(f"Couldn't save some manually added {_type} plugins to database: {err}")
|
||||
|
||||
if (scheduler_first_start and db_plugins) or changes:
|
||||
generate_external_plugins(SCHEDULER.db.get_plugins(_type=_type, with_data=True), original_path=plugin_path)
|
||||
|
||||
check_plugin_changes("external")
|
||||
check_plugin_changes("pro")
|
||||
|
|
@ -531,12 +548,12 @@ if __name__ == "__main__":
|
|||
if not SCHEDULER.run_single("download-pro-plugins"):
|
||||
logger.warning("download-pro-plugins job failed at first start, pro plugins settings set by the user may not be up to date ...")
|
||||
|
||||
changes = db.check_changes()
|
||||
changes = SCHEDULER.db.check_changes()
|
||||
if INTEGRATION not in ("Swarm", "Kubernetes", "Autoconf") and (changes["pro_plugins_changed"] or changes["external_plugins_changed"]):
|
||||
if changes["pro_plugins_changed"]:
|
||||
generate_external_plugins(db.get_plugins(_type="pro", with_data=True), original_path=PRO_PLUGINS_PATH)
|
||||
generate_external_plugins(SCHEDULER.db.get_plugins(_type="pro", with_data=True), original_path=PRO_PLUGINS_PATH)
|
||||
if changes["external_plugins_changed"]:
|
||||
generate_external_plugins(db.get_plugins(_type="external", with_data=True))
|
||||
generate_external_plugins(SCHEDULER.db.get_plugins(_type="external", with_data=True))
|
||||
|
||||
# run the config saver to save potential ignored external plugins settings
|
||||
logger.info("Running config saver to save potential ignored external plugins settings ...")
|
||||
|
|
@ -557,8 +574,8 @@ if __name__ == "__main__":
|
|||
)
|
||||
|
||||
SCHEDULER.update_jobs()
|
||||
env = db.get_config()
|
||||
env["DATABASE_URI"] = db.database_uri
|
||||
env = SCHEDULER.db.get_config()
|
||||
env["DATABASE_URI"] = SCHEDULER.db.database_uri
|
||||
|
||||
logger.info("Executing scheduler ...")
|
||||
|
||||
|
|
@ -582,7 +599,7 @@ if __name__ == "__main__":
|
|||
else:
|
||||
logger.info(f"Successfully sent {CACHE_PATH} folder")
|
||||
|
||||
def listen_for_instances_reload(db: Database):
|
||||
def listen_for_instances_reload():
|
||||
from docker import DockerClient
|
||||
|
||||
global SCHEDULER
|
||||
|
|
@ -592,12 +609,12 @@ if __name__ == "__main__":
|
|||
if event["Action"] in ("start", "die"):
|
||||
logger.info(f"🐋 Detected {event['Action']} event on container {event['Actor']['Attributes']['name']}")
|
||||
SCHEDULER.auto_setup()
|
||||
db.update_instances([api_to_instance(api) for api in SCHEDULER.apis], changed=event["Action"] == "die")
|
||||
SCHEDULER.db.update_instances([api_to_instance(api) for api in SCHEDULER.apis], changed=event["Action"] == "die")
|
||||
if event["Action"] == "start":
|
||||
db.checked_changes(value=True)
|
||||
SCHEDULER.db.checked_changes(value=True)
|
||||
|
||||
if INTEGRATION == "Docker" and not override_instances:
|
||||
Thread(target=listen_for_instances_reload, args=(db,), name="listen_for_instances_reload").start()
|
||||
Thread(target=listen_for_instances_reload, name="listen_for_instances_reload").start()
|
||||
|
||||
while True:
|
||||
threads.clear()
|
||||
|
|
@ -685,7 +702,7 @@ if __name__ == "__main__":
|
|||
except:
|
||||
logger.error(f"Exception while reloading after running jobs once scheduling : {format_exc()}")
|
||||
|
||||
ret = db.checked_changes(CHANGES)
|
||||
ret = SCHEDULER.db.checked_changes(CHANGES)
|
||||
|
||||
if ret:
|
||||
logger.error(f"An error occurred when setting the changes to checked in the database : {ret}")
|
||||
|
|
@ -700,9 +717,11 @@ if __name__ == "__main__":
|
|||
INSTANCES_NEED_GENERATION = False
|
||||
|
||||
if scheduler_first_start:
|
||||
ret = db.set_scheduler_first_start()
|
||||
ret = SCHEDULER.db.set_scheduler_first_start()
|
||||
|
||||
if ret:
|
||||
if ret == "The database is read-only, the changes will not be saved":
|
||||
logger.warning("The database is read-only, the scheduler first start will not be saved")
|
||||
elif ret:
|
||||
logger.error(f"An error occurred when setting the scheduler first start : {ret}")
|
||||
stop(1)
|
||||
scheduler_first_start = False
|
||||
|
|
@ -725,7 +744,7 @@ if __name__ == "__main__":
|
|||
|
||||
DB_LOCK_FILE.unlink(missing_ok=True)
|
||||
|
||||
changes = db.check_changes()
|
||||
changes = SCHEDULER.db.check_changes()
|
||||
|
||||
if isinstance(changes, str):
|
||||
raise Exception(f"An error occurred when checking for changes in the database : {changes}")
|
||||
|
|
@ -783,22 +802,22 @@ if __name__ == "__main__":
|
|||
|
||||
if CONFIGS_NEED_GENERATION:
|
||||
CHANGES.append("custom_configs")
|
||||
generate_custom_configs(db.get_custom_configs())
|
||||
generate_custom_configs(SCHEDULER.db.get_custom_configs())
|
||||
|
||||
if PLUGINS_NEED_GENERATION:
|
||||
CHANGES.append("external_plugins")
|
||||
generate_external_plugins(db.get_plugins(_type="external", with_data=True))
|
||||
generate_external_plugins(SCHEDULER.db.get_plugins(_type="external", with_data=True))
|
||||
SCHEDULER.update_jobs()
|
||||
|
||||
if PRO_PLUGINS_NEED_GENERATION:
|
||||
CHANGES.append("pro_plugins")
|
||||
generate_external_plugins(db.get_plugins(_type="pro", with_data=True), original_path=PRO_PLUGINS_PATH)
|
||||
generate_external_plugins(SCHEDULER.db.get_plugins(_type="pro", with_data=True), original_path=PRO_PLUGINS_PATH)
|
||||
SCHEDULER.update_jobs()
|
||||
|
||||
if CONFIG_NEED_GENERATION:
|
||||
CHANGES.append("config")
|
||||
env = db.get_config()
|
||||
env["DATABASE_URI"] = db.database_uri
|
||||
env = SCHEDULER.db.get_config()
|
||||
env["DATABASE_URI"] = SCHEDULER.db.database_uri
|
||||
|
||||
except:
|
||||
logger.error(f"Exception while executing scheduler : {format_exc()}")
|
||||
|
|
|
|||
|
|
@ -331,9 +331,9 @@ pytz==2024.1 \
|
|||
# acme
|
||||
# certbot
|
||||
# pyrfc3339
|
||||
requests==2.31.0 \
|
||||
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
|
||||
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
|
||||
requests==2.32.2 \
|
||||
--hash=sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289 \
|
||||
--hash=sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c
|
||||
# via acme
|
||||
schedule==1.2.1 \
|
||||
--hash=sha256:14cdeb083a596aa1de6dc77639a1b2ac8bf6eaafa82b1c9279d3612823063d01 \
|
||||
|
|
@ -341,9 +341,9 @@ schedule==1.2.1 \
|
|||
# via importlib-metadata
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
setuptools==69.5.1 \
|
||||
--hash=sha256:6c1fccdac05a97e598fb0ae3bbed5904ccb317337a51139dcd51453611bbb987 \
|
||||
--hash=sha256:c636ac361bc47580504644275c9ad802c50415c7522212252c033bd15f301f32
|
||||
setuptools==70.0.0 \
|
||||
--hash=sha256:54faa7f2e8d2d11bcd2c07bed282eef1046b5c080d1c32add737d7b5817b1ad4 \
|
||||
--hash=sha256:f211a66637b8fa059bb28183da127d4e86396c991a942b028c6650d4319c3fd0
|
||||
# via -r requirements.in
|
||||
six==1.16.0 \
|
||||
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
|
||||
|
|
@ -353,9 +353,9 @@ urllib3==2.2.1 \
|
|||
--hash=sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d \
|
||||
--hash=sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19
|
||||
# via requests
|
||||
zipp==3.18.1 \
|
||||
--hash=sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b \
|
||||
--hash=sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715
|
||||
zipp==3.18.2 \
|
||||
--hash=sha256:6278d9ddbcfb1f1089a88fde84481528b07b0e10474e09dcfe53dad4069fa059 \
|
||||
--hash=sha256:dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e
|
||||
# via
|
||||
# acme
|
||||
# certbot
|
||||
|
|
|
|||
|
|
@ -62,7 +62,8 @@ RUN apk add --no-cache bash unzip libmagic mariadb-client postgresql-client sqli
|
|||
ln -s /proc/1/fd/2 /var/log/bunkerweb/ui.log
|
||||
|
||||
# Fix CVEs
|
||||
# There are no CVEs to fix for this image
|
||||
RUN apk add --no-cache "busybox>=1.36.1-r17" "busybox-binsh>=1.36.1-r17" "ssl_client>=1.36.1-r17" # CVE-2023-42363 CVE-2023-42364 CVE-2023-42365 CVE-2023-42366
|
||||
RUN apk add --no-cache "libcrypto3>=3.1.5-r0" "libssl3>=3.1.5-r0" # CVE-2024-4603
|
||||
|
||||
LABEL maintainer "Bunkerity <contact@bunkerity.com>"
|
||||
LABEL version "1.5.7"
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ REVERSE_PROXY_PATH = re_compile(r"^(?P<host>https?://.{1,255}(:((6553[0-5])|(655
|
|||
|
||||
def wait_applying():
|
||||
for i in range(31):
|
||||
curr_changes = db.check_changes()
|
||||
curr_changes = app.config["DB"].check_changes()
|
||||
if isinstance(curr_changes, str):
|
||||
app.logger.error(f"An error occurred when checking for changes in the database : {curr_changes}")
|
||||
elif not any(curr_changes.values()):
|
||||
|
|
@ -197,9 +197,9 @@ def get_ui_data():
|
|||
return ui_data
|
||||
|
||||
|
||||
def manage_bunkerweb(method: str, *args, operation: str = "reloads", is_draft: bool = False, was_draft: bool = False, threaded: bool = False):
|
||||
def manage_bunkerweb(method: str, *args, operation: str = "reloads", is_draft: bool = False, was_draft: bool = False, threaded: bool = False) -> int:
|
||||
# Do the operation
|
||||
error = False
|
||||
error = 0
|
||||
ui_data = get_ui_data()
|
||||
|
||||
if "TO_FLASH" not in ui_data:
|
||||
|
|
@ -213,19 +213,15 @@ def manage_bunkerweb(method: str, *args, operation: str = "reloads", is_draft: b
|
|||
elif operation == "delete":
|
||||
operation, error = app.config["CONFIG"].delete_service(args[2], check_changes=(was_draft != is_draft or not is_draft))
|
||||
|
||||
if error:
|
||||
ui_data["TO_FLASH"].append({"content": operation, "type": "error"})
|
||||
else:
|
||||
ui_data["TO_FLASH"].append({"content": operation, "type": "success"})
|
||||
|
||||
if not error:
|
||||
if was_draft != is_draft or not is_draft:
|
||||
# update changes in db
|
||||
ret = db.checked_changes(["config", "custom_configs"], value=True)
|
||||
ret = app.config["DB"].checked_changes(["config", "custom_configs"], value=True)
|
||||
if ret:
|
||||
app.logger.error(f"Couldn't set the changes to checked in the database: {ret}")
|
||||
ui_data["TO_FLASH"].append({"content": f"An error occurred when setting the changes to checked in the database : {ret}", "type": "error"})
|
||||
elif method == "global_config":
|
||||
operation = app.config["CONFIG"].edit_global_conf(args[0])
|
||||
operation, error = app.config["CONFIG"].edit_global_conf(args[0])
|
||||
|
||||
if operation == "reload":
|
||||
operation = app.config["INSTANCES"].reload_instance(args[0])
|
||||
|
|
@ -237,14 +233,12 @@ def manage_bunkerweb(method: str, *args, operation: str = "reloads", is_draft: b
|
|||
operation = app.config["INSTANCES"].restart_instance(args[0])
|
||||
elif not error:
|
||||
operation = "The scheduler will be in charge of reloading the instances."
|
||||
else:
|
||||
operation = ""
|
||||
|
||||
if operation:
|
||||
if isinstance(operation, list):
|
||||
for op in operation:
|
||||
ui_data["TO_FLASH"].append({"content": f"Reload failed for the instance {op}", "type": "error"})
|
||||
elif operation.startswith("Can't"):
|
||||
elif operation.startswith(("Can't", "The database is read-only")):
|
||||
ui_data["TO_FLASH"].append({"content": operation, "type": "error"})
|
||||
else:
|
||||
ui_data["TO_FLASH"].append({"content": operation, "type": "success"})
|
||||
|
|
@ -262,16 +256,18 @@ def manage_bunkerweb(method: str, *args, operation: str = "reloads", is_draft: b
|
|||
with LOCK:
|
||||
TMP_DATA_FILE.write_text(dumps(ui_data), encoding="utf-8")
|
||||
|
||||
return error
|
||||
|
||||
|
||||
# UTILS
|
||||
def run_action(plugin: str, function_name: str = ""):
|
||||
message = ""
|
||||
module = db.get_plugin_actions(plugin)
|
||||
module = app.config["DB"].get_plugin_actions(plugin)
|
||||
|
||||
if module is None:
|
||||
return {"status": "ko", "code": 404, "message": "The actions.py file for the plugin does not exist"}
|
||||
|
||||
obfuscation = db.get_plugin_obfuscation(plugin)
|
||||
obfuscation = app.config["DB"].get_plugin_obfuscation(plugin)
|
||||
tmp_dir = None
|
||||
|
||||
try:
|
||||
|
|
@ -382,9 +378,9 @@ def error_message(msg: str):
|
|||
@app.context_processor
|
||||
def inject_variables():
|
||||
ui_data = get_ui_data()
|
||||
metadata = db.get_metadata()
|
||||
metadata = app.config["DB"].get_metadata()
|
||||
|
||||
curr_changes = db.check_changes()
|
||||
curr_changes = app.config["DB"].check_changes()
|
||||
|
||||
if ui_data.get("PRO_LOADING") and not any(curr_changes.values()):
|
||||
ui_data["PRO_LOADING"] = False
|
||||
|
|
@ -402,6 +398,7 @@ def inject_variables():
|
|||
plugins=app.config["CONFIG"].get_plugins(),
|
||||
pro_loading=ui_data.get("PRO_LOADING", False),
|
||||
bw_version=metadata["version"],
|
||||
is_readonly=app.config["DB"].readonly,
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -448,7 +445,11 @@ def handle_csrf_error(_):
|
|||
|
||||
@app.before_request
|
||||
def before_request():
|
||||
db_user = db.get_ui_user()
|
||||
try:
|
||||
db_user = app.config["DB"].get_ui_user()
|
||||
except BaseException:
|
||||
db_user = app.config["DB"].get_ui_user()
|
||||
|
||||
if db_user:
|
||||
app.config["USER"] = User(**db_user)
|
||||
|
||||
|
|
@ -465,7 +466,9 @@ def before_request():
|
|||
and not request.path.startswith(("/css", "/images", "/js", "/json", "/webfonts"))
|
||||
and request.path.endswith("/login")
|
||||
):
|
||||
return redirect(url_for("login", next=request.path))
|
||||
logout_user()
|
||||
session.clear()
|
||||
return redirect(url_for("login"))
|
||||
|
||||
# Case not login page, keep on 2FA before any other access
|
||||
if (
|
||||
|
|
@ -551,7 +554,7 @@ def setup():
|
|||
if not app.config["USER"]:
|
||||
app.config["USER"] = User(request.form["admin_username"], request.form["admin_password"], method="ui")
|
||||
|
||||
ret = db.create_ui_user(request.form["admin_username"], app.config["USER"].password_hash, method="ui")
|
||||
ret = app.config["DB"].create_ui_user(request.form["admin_username"], app.config["USER"].password_hash, method="ui")
|
||||
if ret:
|
||||
return redirect_flash_error(f"Couldn't create the admin user in the database: {ret}", "setup", False, "error")
|
||||
|
||||
|
|
@ -674,7 +677,7 @@ def home():
|
|||
version=bw_version,
|
||||
instances_number=len(instances),
|
||||
services_number=services,
|
||||
plugins_errors=db.get_plugins_errors(),
|
||||
plugins_errors=app.config["DB"].get_plugins_errors(),
|
||||
instance_health_count=instance_health_count,
|
||||
services_scheduler_count=services_scheduler_count,
|
||||
services_ui_count=services_ui_count,
|
||||
|
|
@ -709,19 +712,22 @@ def account():
|
|||
|
||||
# Force job to contact PRO API
|
||||
# by setting the last check to None
|
||||
metadata = db.get_metadata()
|
||||
metadata = app.config["DB"].get_metadata()
|
||||
metadata["last_pro_check"] = None
|
||||
db.set_pro_metadata(metadata)
|
||||
app.config["DB"].set_pro_metadata(metadata)
|
||||
|
||||
flash("Checking license key to upgrade.", "success")
|
||||
|
||||
curr_changes = db.check_changes()
|
||||
curr_changes = app.config["DB"].check_changes()
|
||||
|
||||
# Reload instances
|
||||
def update_global_config(threaded: bool = False):
|
||||
wait_applying()
|
||||
|
||||
manage_bunkerweb("global_config", variable, threaded=threaded)
|
||||
if not manage_bunkerweb("global_config", variable, threaded=threaded):
|
||||
message = "Checking license key to upgrade."
|
||||
if threaded:
|
||||
ui_data["TO_FLASH"].append({"content": message, "type": "success"})
|
||||
else:
|
||||
flash(message)
|
||||
|
||||
ui_data = get_ui_data()
|
||||
ui_data["PRO_LOADING"] = True
|
||||
|
|
@ -795,7 +801,7 @@ def account():
|
|||
TMP_DATA_FILE.write_text(dumps(ui_data), encoding="utf-8")
|
||||
|
||||
user = User(username, password, is_two_factor_enabled=is_two_factor_enabled, secret_token=secret_token, method=current_user.method)
|
||||
ret = db.update_ui_user(
|
||||
ret = app.config["DB"].update_ui_user(
|
||||
username, user.password_hash, is_two_factor_enabled, secret_token, current_user.method if request.form["operation"] == "totp" else "ui"
|
||||
)
|
||||
if ret:
|
||||
|
|
@ -988,7 +994,7 @@ def services():
|
|||
|
||||
error = 0
|
||||
|
||||
curr_changes = db.check_changes()
|
||||
curr_changes = app.config["DB"].check_changes()
|
||||
|
||||
old_server_name = request.form.get("OLD_SERVER_NAME", "")
|
||||
operation = request.form["operation"]
|
||||
|
|
@ -1118,7 +1124,7 @@ def global_config():
|
|||
if setting and setting["global"] and (setting["value"] != value or setting["value"] == config.get(variable, {"value": None})["value"]):
|
||||
variables[f"{service}_{variable}"] = value
|
||||
|
||||
curr_changes = db.check_changes()
|
||||
curr_changes = app.config["DB"].check_changes()
|
||||
|
||||
def update_global_config(threaded: bool = False):
|
||||
wait_applying()
|
||||
|
|
@ -1165,7 +1171,7 @@ def global_config():
|
|||
@app.route("/configs", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def configs():
|
||||
db_configs = db.get_custom_configs()
|
||||
db_configs = app.config["DB"].get_custom_configs()
|
||||
|
||||
if request.method == "POST":
|
||||
operation = ""
|
||||
|
|
@ -1257,7 +1263,7 @@ def configs():
|
|||
del db_configs[index]
|
||||
operation = f"Deleted config {name}{f' for service {service_id}' if service_id else ''}"
|
||||
|
||||
error = db.save_custom_configs([config for config in db_configs if config["method"] == "ui"], "ui")
|
||||
error = app.config["DB"].save_custom_configs([config for config in db_configs if config["method"] == "ui"], "ui")
|
||||
if error:
|
||||
app.logger.error(f"Could not save custom configs: {error}")
|
||||
return redirect_flash_error("Couldn't save custom configs", "configs", True)
|
||||
|
|
@ -1298,7 +1304,7 @@ def plugins():
|
|||
if variables["type"] in ("core", "pro"):
|
||||
return redirect_flash_error(f"Can't delete {variables['type']} plugin {variables['name']}", "plugins", True)
|
||||
|
||||
curr_changes = db.check_changes()
|
||||
curr_changes = app.config["DB"].check_changes()
|
||||
|
||||
def update_plugins(threaded: bool = False): # type: ignore
|
||||
wait_applying()
|
||||
|
|
@ -1310,7 +1316,7 @@ def plugins():
|
|||
|
||||
ui_data = get_ui_data()
|
||||
|
||||
err = db.update_external_plugins(plugins)
|
||||
err = app.config["DB"].update_external_plugins(plugins)
|
||||
if err:
|
||||
message = f"Couldn't update external plugins to database: {err}"
|
||||
if threaded:
|
||||
|
|
@ -1507,7 +1513,7 @@ def plugins():
|
|||
if errors >= files_count:
|
||||
return redirect(url_for("loading", next=url_for("plugins")))
|
||||
|
||||
curr_changes = db.check_changes()
|
||||
curr_changes = app.config["DB"].check_changes()
|
||||
|
||||
def update_plugins(threaded: bool = False):
|
||||
wait_applying()
|
||||
|
|
@ -1520,7 +1526,7 @@ def plugins():
|
|||
|
||||
ui_data = get_ui_data()
|
||||
|
||||
err = db.update_external_plugins(new_plugins, delete_missing=False)
|
||||
err = app.config["DB"].update_external_plugins(new_plugins, delete_missing=False)
|
||||
if err:
|
||||
message = f"Couldn't update external plugins to database: {err}"
|
||||
if threaded:
|
||||
|
|
@ -1657,7 +1663,7 @@ def custom_plugin(plugin: str):
|
|||
if request.method == "GET":
|
||||
|
||||
# Check template
|
||||
page = db.get_plugin_template(plugin)
|
||||
page = app.config["DB"].get_plugin_template(plugin)
|
||||
|
||||
if not page:
|
||||
return error_message("The plugin does not have a template"), 404
|
||||
|
|
@ -1790,7 +1796,7 @@ def cache():
|
|||
path_to_dict(
|
||||
join(sep, "var", "cache", "bunkerweb"),
|
||||
is_cache=True,
|
||||
db_data=db.get_jobs_cache_files(),
|
||||
db_data=app.config["DB"].get_jobs_cache_files(),
|
||||
services=app.config["CONFIG"].get_config(methods=False).get("SERVER_NAME", "").split(" "),
|
||||
)
|
||||
],
|
||||
|
|
@ -2139,6 +2145,9 @@ def bans():
|
|||
flash("Couldn't connect to redis, ban list might be incomplete", "error")
|
||||
|
||||
if request.method == "POST":
|
||||
if app.config["DB"].readonly:
|
||||
return redirect_flash_error("Read only mode is enabled", "bans")
|
||||
|
||||
# Check variables
|
||||
is_request_form("bans")
|
||||
|
||||
|
|
@ -2246,7 +2255,7 @@ def bans():
|
|||
@app.route("/jobs", methods=["GET"])
|
||||
@login_required
|
||||
def jobs():
|
||||
return render_template("jobs.html", jobs=db.get_jobs(), jobs_errors=db.get_plugins_errors(), username=current_user.get_id())
|
||||
return render_template("jobs.html", jobs=app.config["DB"].get_jobs(), jobs_errors=app.config["DB"].get_plugins_errors(), username=current_user.get_id())
|
||||
|
||||
|
||||
@app.route("/jobs/download", methods=["GET"])
|
||||
|
|
@ -2262,7 +2271,7 @@ def jobs_download():
|
|||
|
||||
file_name = secure_filename(file_name)
|
||||
|
||||
cache_file = db.get_job_cache_file(job_name, file_name, service_id=service_id, plugin_id=plugin_id)
|
||||
cache_file = app.config["DB"].get_job_cache_file(job_name, file_name, service_id=service_id, plugin_id=plugin_id)
|
||||
|
||||
if not cache_file:
|
||||
return jsonify({"status": "ko", "message": "file not found"}), 404
|
||||
|
|
|
|||
|
|
@ -9,5 +9,5 @@ pyotp==2.9.0
|
|||
python-magic==0.4.27
|
||||
python_dateutil==2.9.0.post0
|
||||
qrcode==7.4.2
|
||||
regex==2024.5.10
|
||||
regex==2024.5.15
|
||||
werkzeug==3.0.3
|
||||
|
|
|
|||
|
|
@ -169,86 +169,86 @@ qrcode==7.4.2 \
|
|||
--hash=sha256:581dca7a029bcb2deef5d01068e39093e80ef00b4a61098a2182eac59d01643a \
|
||||
--hash=sha256:9dd969454827e127dbd93696b20747239e6d540e082937c90f14ac95b30f5845
|
||||
# via -r requirements.in
|
||||
regex==2024.5.10 \
|
||||
--hash=sha256:031219782d97550c2098d9a68ce9e9eaefe67d2d81d8ff84c8354f9c009e720c \
|
||||
--hash=sha256:0709ba544cf50bd5cb843df4b8bb6701bae2b70a8e88da9add8386cbca5c1385 \
|
||||
--hash=sha256:0a9f89d7db5ef6bdf53e5cc8e6199a493d0f1374b3171796b464a74ebe8e508a \
|
||||
--hash=sha256:0bc94873ba11e34837bffd7e5006703abeffc4514e2f482022f46ce05bd25e67 \
|
||||
--hash=sha256:0ce56a923f4c01d7568811bfdffe156268c0a7aae8a94c902b92fe34c4bde785 \
|
||||
--hash=sha256:0faecb6d5779753a6066a3c7a0471a8d29fe25d9981ca9e552d6d1b8f8b6a594 \
|
||||
--hash=sha256:1118ba9def608250250f4b3e3f48c62f4562ba16ca58ede491b6e7554bfa09ff \
|
||||
--hash=sha256:12446827f43c7881decf2c126762e11425de5eb93b3b0d8b581344c16db7047a \
|
||||
--hash=sha256:14905ed75c7a6edf423eb46c213ed3f4507c38115f1ed3c00f4ec9eafba50e58 \
|
||||
--hash=sha256:15e593386ec6331e0ab4ac0795b7593f02ab2f4b30a698beb89fbdc34f92386a \
|
||||
--hash=sha256:160ba087232c5c6e2a1e7ad08bd3a3f49b58c815be0504d8c8aacfb064491cd8 \
|
||||
--hash=sha256:161a206c8f3511e2f5fafc9142a2cc25d7fe9a1ec5ad9b4ad2496a7c33e1c5d2 \
|
||||
--hash=sha256:169fd0acd7a259f58f417e492e93d0e15fc87592cd1e971c8c533ad5703b5830 \
|
||||
--hash=sha256:193b7c6834a06f722f0ce1ba685efe80881de7c3de31415513862f601097648c \
|
||||
--hash=sha256:1a3903128f9e17a500618e80c68165c78c741ebb17dd1a0b44575f92c3c68b02 \
|
||||
--hash=sha256:1d5bd666466c8f00a06886ce1397ba8b12371c1f1c6d1bef11013e9e0a1464a8 \
|
||||
--hash=sha256:224a9269f133564109ce668213ef3cb32bc72ccf040b0b51c72a50e569e9dc9e \
|
||||
--hash=sha256:236cace6c1903effd647ed46ce6dd5d76d54985fc36dafc5256032886736c85d \
|
||||
--hash=sha256:249fbcee0a277c32a3ce36d8e36d50c27c968fdf969e0fbe342658d4e010fbc8 \
|
||||
--hash=sha256:29d839829209f3c53f004e1de8c3113efce6d98029f044fa5cfee666253ee7e6 \
|
||||
--hash=sha256:2c8982ee19ccecabbaeac1ba687bfef085a6352a8c64f821ce2f43e6d76a9298 \
|
||||
--hash=sha256:2f30a5ab8902f93930dc6f627c4dd5da2703333287081c85cace0fc6e21c25af \
|
||||
--hash=sha256:304e7e2418146ae4d0ef0e9ffa28f881f7874b45b4994cc2279b21b6e7ae50c8 \
|
||||
--hash=sha256:32e5f3b8e32918bfbdd12eca62e49ab3031125c454b507127ad6ecbd86e62fca \
|
||||
--hash=sha256:334b79ce9c08f26b4659a53f42892793948a613c46f1b583e985fd5a6bf1c149 \
|
||||
--hash=sha256:33d19f0cde6838c81acffff25c7708e4adc7dd02896c9ec25c3939b1500a1778 \
|
||||
--hash=sha256:3799e36d60a35162bb35b2246d8bb012192b7437dff807ef79c14e7352706306 \
|
||||
--hash=sha256:42be5de7cc8c1edac55db92d82b68dc8e683b204d6f5414c5a51997a323d7081 \
|
||||
--hash=sha256:44b3267cea873684af022822195298501568ed44d542f9a2d9bebc0212e99069 \
|
||||
--hash=sha256:458d68d34fb74b906709735c927c029e62f7d06437a98af1b5b6258025223210 \
|
||||
--hash=sha256:45cc13d398b6359a7708986386f72bd156ae781c3e83a68a6d4cee5af04b1ce9 \
|
||||
--hash=sha256:4e7eaf9df15423d07b6050fb91f86c66307171b95ea53e2d87a7993b6d02c7f7 \
|
||||
--hash=sha256:4fad420b14ae1970a1f322e8ae84a1d9d89375eb71e1b504060ab2d1bfe68f3c \
|
||||
--hash=sha256:504b5116e2bd1821efd815941edff7535e93372a098e156bb9dffde30264e798 \
|
||||
--hash=sha256:50e7e96a527488334379e05755b210b7da4a60fc5d6481938c1fa053e0c92184 \
|
||||
--hash=sha256:51d27844763c273a122e08a3e86e7aefa54ee09fb672d96a645ece0454d8425e \
|
||||
--hash=sha256:5253dcb0bfda7214523de58b002eb0090cb530d7c55993ce5f6d17faf953ece7 \
|
||||
--hash=sha256:534efd2653ebc4f26fc0e47234e53bf0cb4715bb61f98c64d2774a278b58c846 \
|
||||
--hash=sha256:560278c9975694e1f0bc50da187abf2cdc1e4890739ea33df2bc4a85eeef143e \
|
||||
--hash=sha256:571452362d552de508c37191b6abbbb660028b8b418e2d68c20779e0bc8eaaa8 \
|
||||
--hash=sha256:62b5f7910b639f3c1d122d408421317c351e213ca39c964ad4121f27916631c6 \
|
||||
--hash=sha256:696639a73ca78a380acfaa0a1f6dd8220616a99074c05bba9ba8bb916914b224 \
|
||||
--hash=sha256:6ccdeef4584450b6f0bddd5135354908dacad95425fcb629fe36d13e48b60f32 \
|
||||
--hash=sha256:70364a097437dd0a90b31cd77f09f7387ad9ac60ef57590971f43b7fca3082a5 \
|
||||
--hash=sha256:7117cb7d6ac7f2e985f3d18aa8a1728864097da1a677ffa69e970ca215baebf1 \
|
||||
--hash=sha256:7467ad8b0eac0b28e52679e972b9b234b3de0ea5cee12eb50091d2b68145fe36 \
|
||||
--hash=sha256:7d35d4cc9270944e95f9c88af757b0c9fc43f396917e143a5756608462c5223b \
|
||||
--hash=sha256:7dda3091838206969c2b286f9832dff41e2da545b99d1cfaea9ebd8584d02708 \
|
||||
--hash=sha256:853cc36e756ff673bf984e9044ccc8fad60b95a748915dddeab9488aea974c73 \
|
||||
--hash=sha256:8722f72068b3e1156a4b2e1afde6810f1fc67155a9fa30a4b9d5b4bc46f18fb0 \
|
||||
--hash=sha256:8c6c71cf92b09e5faa72ea2c68aa1f61c9ce11cb66fdc5069d712f4392ddfd00 \
|
||||
--hash=sha256:903350bf44d7e4116b4d5898b30b15755d61dcd3161e3413a49c7db76f0bee5a \
|
||||
--hash=sha256:91b53dea84415e8115506cc62e441a2b54537359c63d856d73cb1abe05af4c9a \
|
||||
--hash=sha256:951be1eae7b47660412dc4938777a975ebc41936d64e28081bf2e584b47ec246 \
|
||||
--hash=sha256:972b49f2fe1047b9249c958ec4fa1bdd2cf8ce305dc19d27546d5a38e57732d8 \
|
||||
--hash=sha256:9a8625849387b9d558d528e263ecc9c0fbde86cfa5c2f0eef43fff480ae24d71 \
|
||||
--hash=sha256:9cdbb1998da94607d5eec02566b9586f0e70d6438abf1b690261aac0edda7ab6 \
|
||||
--hash=sha256:9e6d4d6ae1827b2f8c7200aaf7501c37cf3f3896c86a6aaf2566448397c823dd \
|
||||
--hash=sha256:aab65121229c2ecdf4a31b793d99a6a0501225bd39b616e653c87b219ed34a49 \
|
||||
--hash=sha256:ab98016541543692a37905871a5ffca59b16e08aacc3d7d10a27297b443f572d \
|
||||
--hash=sha256:ad45f3bccfcb00868f2871dce02a755529838d2b86163ab8a246115e80cfb7d6 \
|
||||
--hash=sha256:b43b78f9386d3d932a6ce5af4b45f393d2e93693ee18dc4800d30a8909df700e \
|
||||
--hash=sha256:b66421f8878a0c82fc0c272a43e2121c8d4c67cb37429b764f0d5ad70b82993b \
|
||||
--hash=sha256:ba034c8db4b264ef1601eb33cd23d87c5013b8fb48b8161debe2e5d3bd9156b0 \
|
||||
--hash=sha256:bbdc5db2c98ac2bf1971ffa1410c87ca7a15800415f788971e8ba8520fc0fda9 \
|
||||
--hash=sha256:bc0db93ad039fc2fe32ccd3dd0e0e70c4f3d6e37ae83f0a487e1aba939bd2fbd \
|
||||
--hash=sha256:bf7c8ee4861d9ef5b1120abb75846828c811f932d63311596ad25fa168053e00 \
|
||||
--hash=sha256:bf9596cba92ce7b1fd32c7b07c6e3212c7eed0edc271757e48bfcd2b54646452 \
|
||||
--hash=sha256:c43395a3b7cc9862801a65c6994678484f186ce13c929abab44fb8a9e473a55a \
|
||||
--hash=sha256:c46a76a599fcbf95f98755275c5527304cc4f1bb69919434c1e15544d7052910 \
|
||||
--hash=sha256:ca23b41355ba95929e9505ee04e55495726aa2282003ed9b012d86f857d3e49b \
|
||||
--hash=sha256:cd832bd9b6120d6074f39bdfbb3c80e416848b07ac72910f1c7f03131a6debc3 \
|
||||
--hash=sha256:cfa6d61a76c77610ba9274c1a90a453062bdf6887858afbe214d18ad41cf6bde \
|
||||
--hash=sha256:d8a0f0ab5453e409586b11ebe91c672040bc804ca98d03a656825f7890cbdf88 \
|
||||
--hash=sha256:e91b1976358e17197157b405cab408a5f4e33310cda211c49fc6da7cffd0b2f0 \
|
||||
--hash=sha256:ea057306ab469130167014b662643cfaed84651c792948891d003cf0039223a5 \
|
||||
--hash=sha256:eda3dd46df535da787ffb9036b5140f941ecb91701717df91c9daf64cabef953 \
|
||||
--hash=sha256:f03b1dbd4d9596dd84955bb40f7d885204d6aac0d56a919bb1e0ff2fb7e1735a \
|
||||
--hash=sha256:fa9335674d7c819674467c7b46154196c51efbaf5f5715187fd366814ba3fa39
|
||||
regex==2024.5.15 \
|
||||
--hash=sha256:0721931ad5fe0dda45d07f9820b90b2148ccdd8e45bb9e9b42a146cb4f695649 \
|
||||
--hash=sha256:10002e86e6068d9e1c91eae8295ef690f02f913c57db120b58fdd35a6bb1af35 \
|
||||
--hash=sha256:10e4ce0dca9ae7a66e6089bb29355d4432caed736acae36fef0fdd7879f0b0cb \
|
||||
--hash=sha256:119af6e56dce35e8dfb5222573b50c89e5508d94d55713c75126b753f834de68 \
|
||||
--hash=sha256:1337b7dbef9b2f71121cdbf1e97e40de33ff114801263b275aafd75303bd62b5 \
|
||||
--hash=sha256:13cdaf31bed30a1e1c2453ef6015aa0983e1366fad2667657dbcac7b02f67133 \
|
||||
--hash=sha256:1595f2d10dff3d805e054ebdc41c124753631b6a471b976963c7b28543cf13b0 \
|
||||
--hash=sha256:16093f563098448ff6b1fa68170e4acbef94e6b6a4e25e10eae8598bb1694b5d \
|
||||
--hash=sha256:1878b8301ed011704aea4c806a3cadbd76f84dece1ec09cc9e4dc934cfa5d4da \
|
||||
--hash=sha256:19068a6a79cf99a19ccefa44610491e9ca02c2be3305c7760d3831d38a467a6f \
|
||||
--hash=sha256:19dfb1c504781a136a80ecd1fff9f16dddf5bb43cec6871778c8a907a085bb3d \
|
||||
--hash=sha256:1b5269484f6126eee5e687785e83c6b60aad7663dafe842b34691157e5083e53 \
|
||||
--hash=sha256:1c1c174d6ec38d6c8a7504087358ce9213d4332f6293a94fbf5249992ba54efa \
|
||||
--hash=sha256:2431b9e263af1953c55abbd3e2efca67ca80a3de8a0437cb58e2421f8184717a \
|
||||
--hash=sha256:287eb7f54fc81546346207c533ad3c2c51a8d61075127d7f6d79aaf96cdee890 \
|
||||
--hash=sha256:2b4c884767504c0e2401babe8b5b7aea9148680d2e157fa28f01529d1f7fcf67 \
|
||||
--hash=sha256:35cb514e137cb3488bce23352af3e12fb0dbedd1ee6e60da053c69fb1b29cc6c \
|
||||
--hash=sha256:391d7f7f1e409d192dba8bcd42d3e4cf9e598f3979cdaed6ab11288da88cb9f2 \
|
||||
--hash=sha256:3ad070b823ca5890cab606c940522d05d3d22395d432f4aaaf9d5b1653e47ced \
|
||||
--hash=sha256:3cd7874d57f13bf70078f1ff02b8b0aa48d5b9ed25fc48547516c6aba36f5741 \
|
||||
--hash=sha256:3e507ff1e74373c4d3038195fdd2af30d297b4f0950eeda6f515ae3d84a1770f \
|
||||
--hash=sha256:455705d34b4154a80ead722f4f185b04c4237e8e8e33f265cd0798d0e44825fa \
|
||||
--hash=sha256:4a605586358893b483976cffc1723fb0f83e526e8f14c6e6614e75919d9862cf \
|
||||
--hash=sha256:4babf07ad476aaf7830d77000874d7611704a7fcf68c9c2ad151f5d94ae4bfc4 \
|
||||
--hash=sha256:4eee78a04e6c67e8391edd4dad3279828dd66ac4b79570ec998e2155d2e59fd5 \
|
||||
--hash=sha256:5397de3219a8b08ae9540c48f602996aa6b0b65d5a61683e233af8605c42b0f2 \
|
||||
--hash=sha256:5b5467acbfc153847d5adb21e21e29847bcb5870e65c94c9206d20eb4e99a384 \
|
||||
--hash=sha256:5eaa7ddaf517aa095fa8da0b5015c44d03da83f5bd49c87961e3c997daed0de7 \
|
||||
--hash=sha256:632b01153e5248c134007209b5c6348a544ce96c46005d8456de1d552455b014 \
|
||||
--hash=sha256:64c65783e96e563103d641760664125e91bd85d8e49566ee560ded4da0d3e704 \
|
||||
--hash=sha256:64f18a9a3513a99c4bef0e3efd4c4a5b11228b48aa80743be822b71e132ae4f5 \
|
||||
--hash=sha256:673b5a6da4557b975c6c90198588181029c60793835ce02f497ea817ff647cb2 \
|
||||
--hash=sha256:68811ab14087b2f6e0fc0c2bae9ad689ea3584cad6917fc57be6a48bbd012c49 \
|
||||
--hash=sha256:6e8d717bca3a6e2064fc3a08df5cbe366369f4b052dcd21b7416e6d71620dca1 \
|
||||
--hash=sha256:71a455a3c584a88f654b64feccc1e25876066c4f5ef26cd6dd711308aa538694 \
|
||||
--hash=sha256:72d7a99cd6b8f958e85fc6ca5b37c4303294954eac1376535b03c2a43eb72629 \
|
||||
--hash=sha256:7b59138b219ffa8979013be7bc85bb60c6f7b7575df3d56dc1e403a438c7a3f6 \
|
||||
--hash=sha256:7dbe2467273b875ea2de38ded4eba86cbcbc9a1a6d0aa11dcf7bd2e67859c435 \
|
||||
--hash=sha256:833616ddc75ad595dee848ad984d067f2f31be645d603e4d158bba656bbf516c \
|
||||
--hash=sha256:87e2a9c29e672fc65523fb47a90d429b70ef72b901b4e4b1bd42387caf0d6835 \
|
||||
--hash=sha256:8fe45aa3f4aa57faabbc9cb46a93363edd6197cbc43523daea044e9ff2fea83e \
|
||||
--hash=sha256:9e717956dcfd656f5055cc70996ee2cc82ac5149517fc8e1b60261b907740201 \
|
||||
--hash=sha256:9efa1a32ad3a3ea112224897cdaeb6aa00381627f567179c0314f7b65d354c62 \
|
||||
--hash=sha256:9ff11639a8d98969c863d4617595eb5425fd12f7c5ef6621a4b74b71ed8726d5 \
|
||||
--hash=sha256:a094801d379ab20c2135529948cb84d417a2169b9bdceda2a36f5f10977ebc16 \
|
||||
--hash=sha256:a0981022dccabca811e8171f913de05720590c915b033b7e601f35ce4ea7019f \
|
||||
--hash=sha256:a0bd000c6e266927cb7a1bc39d55be95c4b4f65c5be53e659537537e019232b1 \
|
||||
--hash=sha256:a32b96f15c8ab2e7d27655969a23895eb799de3665fa94349f3b2fbfd547236f \
|
||||
--hash=sha256:a81e3cfbae20378d75185171587cbf756015ccb14840702944f014e0d93ea09f \
|
||||
--hash=sha256:ac394ff680fc46b97487941f5e6ae49a9f30ea41c6c6804832063f14b2a5a145 \
|
||||
--hash=sha256:ada150c5adfa8fbcbf321c30c751dc67d2f12f15bd183ffe4ec7cde351d945b3 \
|
||||
--hash=sha256:b2b6f1b3bb6f640c1a92be3bbfbcb18657b125b99ecf141fb3310b5282c7d4ed \
|
||||
--hash=sha256:b802512f3e1f480f41ab5f2cfc0e2f761f08a1f41092d6718868082fc0d27143 \
|
||||
--hash=sha256:ba68168daedb2c0bab7fd7e00ced5ba90aebf91024dea3c88ad5063c2a562cca \
|
||||
--hash=sha256:bfc4f82cabe54f1e7f206fd3d30fda143f84a63fe7d64a81558d6e5f2e5aaba9 \
|
||||
--hash=sha256:c0c18345010870e58238790a6779a1219b4d97bd2e77e1140e8ee5d14df071aa \
|
||||
--hash=sha256:c3bea0ba8b73b71b37ac833a7f3fd53825924165da6a924aec78c13032f20850 \
|
||||
--hash=sha256:c486b4106066d502495b3025a0a7251bf37ea9540433940a23419461ab9f2a80 \
|
||||
--hash=sha256:c49e15eac7c149f3670b3e27f1f28a2c1ddeccd3a2812cba953e01be2ab9b5fe \
|
||||
--hash=sha256:c6a2b494a76983df8e3d3feea9b9ffdd558b247e60b92f877f93a1ff43d26656 \
|
||||
--hash=sha256:cab12877a9bdafde5500206d1020a584355a97884dfd388af3699e9137bf7388 \
|
||||
--hash=sha256:cac27dcaa821ca271855a32188aa61d12decb6fe45ffe3e722401fe61e323cd1 \
|
||||
--hash=sha256:cdd09d47c0b2efee9378679f8510ee6955d329424c659ab3c5e3a6edea696294 \
|
||||
--hash=sha256:cf2430df4148b08fb4324b848672514b1385ae3807651f3567871f130a728cc3 \
|
||||
--hash=sha256:d0a3d8d6acf0c78a1fff0e210d224b821081330b8524e3e2bc5a68ef6ab5803d \
|
||||
--hash=sha256:d0c0c0003c10f54a591d220997dd27d953cd9ccc1a7294b40a4be5312be8797b \
|
||||
--hash=sha256:d1f059a4d795e646e1c37665b9d06062c62d0e8cc3c511fe01315973a6542e40 \
|
||||
--hash=sha256:d347a741ea871c2e278fde6c48f85136c96b8659b632fb57a7d1ce1872547600 \
|
||||
--hash=sha256:d3ee02d9e5f482cc8309134a91eeaacbdd2261ba111b0fef3748eeb4913e6a2c \
|
||||
--hash=sha256:d99ceffa25ac45d150e30bd9ed14ec6039f2aad0ffa6bb87a5936f5782fc1569 \
|
||||
--hash=sha256:e38a7d4e8f633a33b4c7350fbd8bad3b70bf81439ac67ac38916c4a86b465456 \
|
||||
--hash=sha256:e4682f5ba31f475d58884045c1a97a860a007d44938c4c0895f41d64481edbc9 \
|
||||
--hash=sha256:e5bb9425fe881d578aeca0b2b4b3d314ec88738706f66f219c194d67179337cb \
|
||||
--hash=sha256:e64198f6b856d48192bf921421fdd8ad8eb35e179086e99e99f711957ffedd6e \
|
||||
--hash=sha256:e6662686aeb633ad65be2a42b4cb00178b3fbf7b91878f9446075c404ada552f \
|
||||
--hash=sha256:ec54d5afa89c19c6dd8541a133be51ee1017a38b412b1321ccb8d6ddbeb4cf7d \
|
||||
--hash=sha256:f5b1dff3ad008dccf18e652283f5e5339d70bf8ba7c98bf848ac33db10f7bc7a \
|
||||
--hash=sha256:f8ec0c2fea1e886a19c3bee0cd19d862b3aa75dcdfb42ebe8ed30708df64687a \
|
||||
--hash=sha256:f9ebd0a36102fcad2f03696e8af4ae682793a5d30b46c647eaf280d6cfb32796
|
||||
# via -r requirements.in
|
||||
six==1.16.0 \
|
||||
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
|
||||
|
|
@ -273,7 +273,7 @@ wtforms==3.1.2 \
|
|||
--hash=sha256:bf831c042829c8cdbad74c27575098d541d039b1faa74c771545ecac916f2c07 \
|
||||
--hash=sha256:f8d76180d7239c94c6322f7990ae1216dae3659b7aa1cee94b6318bdffb474b9
|
||||
# via flask-wtf
|
||||
zipp==3.18.1 \
|
||||
--hash=sha256:206f5a15f2af3dbaee80769fb7dc6f249695e940acca08dfb2a4769fe61e538b \
|
||||
--hash=sha256:2884ed22e7d8961de1c9a05142eb69a247f120291bc0206a00a7642f09b5b715
|
||||
zipp==3.18.2 \
|
||||
--hash=sha256:6278d9ddbcfb1f1089a88fde84481528b07b0e10474e09dcfe53dad4069fa059 \
|
||||
--hash=sha256:dce197b859eb796242b0622af1b8beb0a722d52aa2f57133ead08edd5bf5374e
|
||||
# via importlib-metadata
|
||||
|
|
|
|||
|
|
@ -49,10 +49,7 @@ class Config:
|
|||
conf["SERVER_NAME"] = " ".join(servers)
|
||||
conf["DATABASE_URI"] = self.__db.database_uri
|
||||
|
||||
err = self.__db.save_config(conf, "ui", changed=check_changes)
|
||||
|
||||
if err:
|
||||
self.__db.logger.warning(f"Couldn't save config to database : {err}, config may not work as expected")
|
||||
return self.__db.save_config(conf, "ui", changed=check_changes)
|
||||
|
||||
def get_plugins_settings(self) -> dict:
|
||||
return {
|
||||
|
|
@ -139,8 +136,8 @@ class Config:
|
|||
|
||||
return error
|
||||
|
||||
def reload_config(self) -> None:
|
||||
self.__gen_conf(self.get_config(methods=False), self.get_services(methods=False))
|
||||
def reload_config(self) -> Optional[str]:
|
||||
return self.__gen_conf(self.get_config(methods=False), self.get_services(methods=False))
|
||||
|
||||
def new_service(self, variables: dict, is_draft: bool = False) -> Tuple[str, int]:
|
||||
"""Creates a new service from the given variables
|
||||
|
|
@ -167,7 +164,9 @@ class Config:
|
|||
return f"Service {service['SERVER_NAME'].split(' ')[0]} already exists.", 1
|
||||
|
||||
services.append(variables | {"IS_DRAFT": "yes" if is_draft else "no"})
|
||||
self.__gen_conf(self.get_config(methods=False), services, check_changes=not is_draft)
|
||||
ret = self.__gen_conf(self.get_config(methods=False), services, check_changes=not is_draft)
|
||||
if ret:
|
||||
return ret, 1
|
||||
return f"Configuration for {variables['SERVER_NAME'].split(' ')[0]} has been generated.", 0
|
||||
|
||||
def edit_service(self, old_server_name: str, variables: dict, *, check_changes: bool = True, is_draft: bool = False) -> Tuple[str, int]:
|
||||
|
|
@ -205,10 +204,12 @@ class Config:
|
|||
if k.startswith(old_server_name_splitted[0]):
|
||||
config.pop(k)
|
||||
|
||||
self.__gen_conf(config, services, check_changes=check_changes, changed_service=variables["SERVER_NAME"])
|
||||
ret = self.__gen_conf(config, services, check_changes=check_changes, changed_service=variables["SERVER_NAME"])
|
||||
if ret:
|
||||
return ret, 1
|
||||
return f"Configuration for {old_server_name_splitted[0]} has been edited.", 0
|
||||
|
||||
def edit_global_conf(self, variables: dict) -> str:
|
||||
def edit_global_conf(self, variables: dict) -> Tuple[str, int]:
|
||||
"""Edits the global conf
|
||||
|
||||
Parameters
|
||||
|
|
@ -221,8 +222,10 @@ class Config:
|
|||
str
|
||||
the confirmation message
|
||||
"""
|
||||
self.__gen_conf(self.get_config(methods=False) | variables, self.get_services(methods=False))
|
||||
return "The global configuration has been edited."
|
||||
ret = self.__gen_conf(self.get_config(methods=False) | variables, self.get_services(methods=False))
|
||||
if ret:
|
||||
return ret, 1
|
||||
return "The global configuration has been edited.", 0
|
||||
|
||||
def delete_service(self, service_name: str, *, check_changes: bool = True) -> Tuple[str, int]:
|
||||
"""Deletes a service
|
||||
|
|
@ -269,5 +272,7 @@ class Config:
|
|||
if k in service:
|
||||
service.pop(k)
|
||||
|
||||
self.__gen_conf(new_env, new_services, check_changes=check_changes)
|
||||
ret = self.__gen_conf(new_env, new_services, check_changes=check_changes)
|
||||
if ret:
|
||||
return ret, 1
|
||||
return f"Configuration for {service_name} has been deleted.", 0
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from contextlib import suppress
|
||||
from json import loads
|
||||
from glob import glob
|
||||
from os import sep
|
||||
|
|
@ -7,7 +8,7 @@ from os.path import join
|
|||
def get_ui_templates():
|
||||
ui_templates = []
|
||||
for template_file in glob(join(sep, "usr", "share", "bunkerweb", "templates", "*.json")):
|
||||
try:
|
||||
with suppress(BaseException): # TODO: log exceptions
|
||||
ui_template = {}
|
||||
with open(template_file, "r") as f:
|
||||
bw_template = loads(f.read())
|
||||
|
|
@ -23,9 +24,5 @@ def get_ui_templates():
|
|||
ui_step["settings"].append(ui_setting)
|
||||
ui_template["steps"].append(ui_step)
|
||||
ui_templates.append(ui_template)
|
||||
except Exception as e:
|
||||
# print(e)
|
||||
# TODO: log
|
||||
pass
|
||||
# print(ui_templates, flush=True)
|
||||
return ui_templates
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -513,38 +513,84 @@ class Banner {
|
|||
|
||||
class Clipboard {
|
||||
constructor() {
|
||||
this.isCopy = false;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
// Show clipboard copy if https
|
||||
window.addEventListener("load", () => {
|
||||
// Show clipboard copy if https and has permissions
|
||||
window.addEventListener("load", async () => {
|
||||
if (!window.location.href.startsWith("https://")) return;
|
||||
|
||||
document.querySelectorAll("[data-clipboard-copy]").forEach((el) => {
|
||||
el.classList.remove("hidden");
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener("click", (e) => {
|
||||
window.addEventListener("click", async (e) => {
|
||||
if (!e.target.hasAttribute("data-clipboard-target")) return;
|
||||
this.isCopy = false;
|
||||
// With Chrome
|
||||
try {
|
||||
navigator.permissions
|
||||
.query({ name: "clipboard-write" })
|
||||
.then((result) => {
|
||||
if (result.state === "granted" || result.state === "prompt") {
|
||||
/* write to the clipboard now */
|
||||
const copyEl = document.querySelector(
|
||||
e.target.getAttribute("data-clipboard-target"),
|
||||
);
|
||||
|
||||
navigator.permissions
|
||||
.query({ name: "clipboard-write" })
|
||||
.then((result) => {
|
||||
if (result.state === "granted" || result.state === "prompt") {
|
||||
/* write to the clipboard now */
|
||||
const copyEl = document.querySelector(
|
||||
e.target.getAttribute("data-clipboard-target"),
|
||||
);
|
||||
copyEl.select();
|
||||
copyEl.setSelectionRange(0, 99999); // For mobile devices
|
||||
|
||||
copyEl.select();
|
||||
copyEl.setSelectionRange(0, 99999); // For mobile devices
|
||||
// Copy the text inside the text field
|
||||
|
||||
// Copy the text inside the text field
|
||||
navigator.clipboard.writeText(copyEl.value);
|
||||
}
|
||||
});
|
||||
navigator.clipboard.writeText(copyEl.value);
|
||||
// Stop selecting
|
||||
copyEl.blur();
|
||||
this.isCopy = true;
|
||||
}
|
||||
});
|
||||
} catch (e) {}
|
||||
// With Firefox
|
||||
try {
|
||||
if (this.isCopy) return;
|
||||
/* write to the clipboard now */
|
||||
const copyEl = document.querySelector(
|
||||
e.target.getAttribute("data-clipboard-target"),
|
||||
);
|
||||
|
||||
copyEl.select();
|
||||
copyEl.setSelectionRange(0, 99999); // For mobile devices
|
||||
|
||||
// Copy the text inside the text field
|
||||
|
||||
navigator.clipboard.writeText(copyEl.value);
|
||||
// Stop selecting
|
||||
copyEl.blur();
|
||||
this.isCopy = true;
|
||||
} catch (e) {}
|
||||
// Default
|
||||
try {
|
||||
if (this.isCopy) return;
|
||||
/* write to the clipboard now */
|
||||
const copyEl = document.querySelector(
|
||||
e.target.getAttribute("data-clipboard-target"),
|
||||
);
|
||||
|
||||
copyEl.select();
|
||||
copyEl.setSelectionRange(0, 99999); // For mobile devices
|
||||
|
||||
// Copy the text inside the text field
|
||||
|
||||
navigator.clipboard.writeText(copyEl.value);
|
||||
// Stop selecting
|
||||
copyEl.blur();
|
||||
|
||||
document.execCommand("copy");
|
||||
|
||||
this.isCopy = true;
|
||||
} catch (e) {}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,12 @@ class Multiple {
|
|||
constructor(prefix) {
|
||||
this.prefix = prefix;
|
||||
this.container = document.querySelector("main");
|
||||
this.isReadonly =
|
||||
document
|
||||
.querySelector("[data-global-is-readonly]")
|
||||
.getAttribute("data-global-is-readonly") === "true"
|
||||
? true
|
||||
: false;
|
||||
this.init();
|
||||
}
|
||||
|
||||
|
|
@ -185,7 +191,9 @@ class Multiple {
|
|||
? true
|
||||
: false;
|
||||
|
||||
return proDisabled;
|
||||
if (proDisabled || this.isReadonly) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
sortMultipleByContainerAndSuffixe(obj) {
|
||||
|
|
@ -543,6 +551,7 @@ class Multiple {
|
|||
//for already existing global config multiples
|
||||
//global is check
|
||||
setDisabledMultServ(inp, method, global) {
|
||||
if (o) return inp.setAttribute("disabled", "");
|
||||
// Check if pro
|
||||
const proDisabled = inp
|
||||
.closest("[data-plugin-item]")
|
||||
|
|
|
|||
2
src/ui/static/js/lottie-web.min.js
vendored
2
src/ui/static/js/lottie-web.min.js
vendored
File diff suppressed because one or more lines are too long
|
|
@ -24,6 +24,11 @@ class SettingsService {
|
|||
this.settingsMultiple,
|
||||
"services",
|
||||
);
|
||||
this.isReadOnly =
|
||||
document
|
||||
.querySelector("[data-services-modal]")
|
||||
.getAttribute("data-readonly") === "true";
|
||||
|
||||
this.initSettingsService();
|
||||
}
|
||||
|
||||
|
|
@ -96,6 +101,7 @@ class SettingsService {
|
|||
operation,
|
||||
] = this.getActionData(e.target);
|
||||
|
||||
const forceDisabled = this.isReadOnly ? true : false;
|
||||
const forceEnabled = action === "new" ? true : false;
|
||||
const setMethodUI =
|
||||
action === "new" || action === "clone" ? true : false;
|
||||
|
|
@ -110,6 +116,7 @@ class SettingsService {
|
|||
setMethodUI,
|
||||
forceEnabled,
|
||||
emptyServerName,
|
||||
forceDisabled,
|
||||
);
|
||||
}
|
||||
} catch (err) {}
|
||||
|
|
@ -121,6 +128,10 @@ class ServiceModal {
|
|||
constructor() {
|
||||
//modal elements
|
||||
this.modal = document.querySelector("[data-services-modal]");
|
||||
this.isReadOnly =
|
||||
document
|
||||
.querySelector("[data-services-modal]")
|
||||
.getAttribute("data-readonly") === "true";
|
||||
this.modalTitle = this.modal.querySelector("[data-services-modal-title]");
|
||||
this.modalTabs = this.modal.querySelector(["[data-services-tabs-select]"]);
|
||||
this.modalTabsHeader = this.modal.querySelector([
|
||||
|
|
@ -235,8 +246,8 @@ class ServiceModal {
|
|||
this.setCardViewportHeight(action === "delete" ? false : true);
|
||||
this.setHeaderActionsVisible(action === "delete" ? false : true);
|
||||
this.SetSelectTabsVisible(action === "delete" ? false : true);
|
||||
this.resetFilterSettings();
|
||||
if (action === "edit" || action === "new" || action === "clone") {
|
||||
this.resetFilterSettings();
|
||||
this.formNewEdit.classList.remove("hidden");
|
||||
|
||||
const oldNameValue = action === "edit" ? oldServName : "";
|
||||
|
|
@ -262,7 +273,10 @@ class ServiceModal {
|
|||
}
|
||||
|
||||
this.setIsDraft(isDraft === "yes" ? true : false, method);
|
||||
this.openModal();
|
||||
setTimeout(() => {
|
||||
this.setActionBtns();
|
||||
this.openModal();
|
||||
}, 50);
|
||||
}
|
||||
|
||||
resetFilterSettings() {
|
||||
|
|
@ -279,6 +293,20 @@ class ServiceModal {
|
|||
inpKeyword.dispatchEvent(new Event("input"));
|
||||
}
|
||||
|
||||
setActionBtns() {
|
||||
if (this.isReadOnly) {
|
||||
this.modal.querySelectorAll("button[type='submit']").forEach((btn) => {
|
||||
btn.setAttribute("disabled", "true");
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.isReadOnly) {
|
||||
this.modal.querySelectorAll("button[type='submit']").forEach((btn) => {
|
||||
btn.removeAttribute("disabled");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setIsDraft(isDraft, method) {
|
||||
const draftVal = isDraft ? "yes" : "no";
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ class FolderNav {
|
|||
`[data-${this.prefix}-breadcrumb]`,
|
||||
);
|
||||
this.container = document.querySelector(`[data-${this.prefix}-container]`);
|
||||
this.isReadonly =
|
||||
document
|
||||
.querySelector(`[data-${this.prefix}-container]`)
|
||||
.getAttribute(`data-readonly`) === "true"
|
||||
? true
|
||||
: false;
|
||||
this.listContainer = document.querySelector(
|
||||
`[data-${this.prefix}-folders]`,
|
||||
);
|
||||
|
|
@ -95,6 +101,9 @@ class FolderNav {
|
|||
updateActions(folder) {
|
||||
// for root
|
||||
if (!folder) return this.addFileEl.setAttribute("disabled", "");
|
||||
|
||||
if (folder && this.isReadonly)
|
||||
return this.addFileEl.setAttribute("disabled", "");
|
||||
//check if folder allow add file/folder
|
||||
const isAddFile = folder.getAttribute("data-can-create-file") || "False";
|
||||
isAddFile === "True"
|
||||
|
|
@ -282,6 +291,12 @@ class FolderDropdown {
|
|||
|
||||
class FolderEditor {
|
||||
constructor() {
|
||||
this.isReadonly =
|
||||
document
|
||||
.querySelector(`[data-global-is-readonly]`)
|
||||
.getAttribute(`data-global-is-readonly`) === "true"
|
||||
? true
|
||||
: false;
|
||||
this.editor = ace.edit("editor");
|
||||
this.darkMode = document.querySelector("[data-dark-toggle]");
|
||||
this.initEditor();
|
||||
|
|
@ -291,6 +306,7 @@ class FolderEditor {
|
|||
initEditor() {
|
||||
//editor options
|
||||
this.editor.setShowPrintMargin(false);
|
||||
this.editor.setReadOnly(this.isReadonly);
|
||||
this.setDarkMode();
|
||||
}
|
||||
|
||||
|
|
@ -315,10 +331,6 @@ class FolderEditor {
|
|||
? this.editor.setTheme("ace/theme/dracula")
|
||||
: this.editor.setTheme("ace/theme/dawn");
|
||||
}
|
||||
|
||||
readOnlyBool(bool) {
|
||||
this.editor.setReadOnly(bool);
|
||||
}
|
||||
}
|
||||
|
||||
class FolderModal {
|
||||
|
|
@ -326,6 +338,12 @@ class FolderModal {
|
|||
this.prefix = prefix;
|
||||
//container
|
||||
this.container = document.querySelector(`[data-${this.prefix}-container]`);
|
||||
this.isReadonly =
|
||||
document
|
||||
.querySelector(`[data-${this.prefix}-container]`)
|
||||
.getAttribute(`data-readonly`) === "true"
|
||||
? true
|
||||
: false;
|
||||
//add service/file elements
|
||||
this.breadContainer = document.querySelector(
|
||||
`[data-${this.prefix}-breadcrumb]`,
|
||||
|
|
@ -580,28 +598,30 @@ class FolderModal {
|
|||
if (action === "new") {
|
||||
this.modalSubmit.textContent = "add";
|
||||
this.setSubmitBtnType("valid-btn");
|
||||
return;
|
||||
}
|
||||
if (action === "view") {
|
||||
this.modalSubmit.textContent = "ok";
|
||||
this.setSubmitBtnType("valid-btn");
|
||||
return;
|
||||
}
|
||||
if (action === "edit") {
|
||||
this.setSubmitBtnType("edit-btn");
|
||||
this.modalSubmit.textContent = "edit";
|
||||
return;
|
||||
}
|
||||
|
||||
if (action === "delete") {
|
||||
this.setSubmitBtnType("delete-btn");
|
||||
this.modalSubmit.textContent = "delete";
|
||||
return;
|
||||
}
|
||||
if (action === "download") {
|
||||
this.setSubmitBtnType("info-btn");
|
||||
this.modalSubmit.textContent = "download";
|
||||
return;
|
||||
}
|
||||
|
||||
// readonly logic
|
||||
if (["new", "edit", "delete"].includes(action) && this.isReadonly) {
|
||||
this.modalSubmit.setAttribute("disabled", "true");
|
||||
} else {
|
||||
this.modalSubmit.removeAttribute("disabled");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -649,8 +669,10 @@ class FolderModal {
|
|||
|
||||
//UTILS
|
||||
disabledDOMInpt(bool) {
|
||||
this.modalPathName.disabled = bool;
|
||||
ace.edit("editor").setReadOnly(bool);
|
||||
if (this.isReadonly) ace.edit("editor").setReadOnly(true);
|
||||
if (this.isReadonly) this.modalPathName.disabled = true;
|
||||
if (!this.isReadonly) this.modalPathName.disabled = bool;
|
||||
if (!this.isReadonly) ace.edit("editor").setReadOnly(bool);
|
||||
}
|
||||
|
||||
closeModal() {
|
||||
|
|
|
|||
|
|
@ -268,15 +268,23 @@ class Password {
|
|||
|
||||
class DisabledPop {
|
||||
constructor() {
|
||||
this.isReadonly =
|
||||
document
|
||||
.querySelector("[data-global-is-readonly]")
|
||||
.getAttribute("data-global-is-readonly") === "true"
|
||||
? true
|
||||
: false;
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
window.addEventListener("pointerover", (e) => {
|
||||
if (this.isReadonly) return;
|
||||
//for checkbox and regular inputs
|
||||
if (
|
||||
e.target.tagName === "INPUT" &&
|
||||
e.target.hasAttribute("data-default-method")
|
||||
(e.target.tagName === "INPUT" &&
|
||||
e.target.hasAttribute("data-default-method")) ||
|
||||
e.target.hasAttribute("data-method")
|
||||
) {
|
||||
const el = e.target;
|
||||
this.showPopup(el, "input");
|
||||
|
|
@ -292,6 +300,8 @@ class DisabledPop {
|
|||
});
|
||||
|
||||
window.addEventListener("pointerout", (e) => {
|
||||
if (this.isReadonly) return;
|
||||
|
||||
try {
|
||||
const popupEl = e.target
|
||||
.closest("div")
|
||||
|
|
@ -308,7 +318,8 @@ class DisabledPop {
|
|||
.querySelector("button[data-setting-password]")
|
||||
? true
|
||||
: false;
|
||||
const method = el.getAttribute("data-default-method");
|
||||
const method =
|
||||
el.getAttribute("data-method") || el.getAttribute("data-default-method");
|
||||
const popupHTML = `
|
||||
<div data-disabled-info class="${
|
||||
type === "select" ? "translate-y-2" : ""
|
||||
|
|
|
|||
|
|
@ -1000,6 +1000,7 @@ class Settings {
|
|||
this.oldServName = "";
|
||||
this.setMethodUI = false;
|
||||
this.forceEnabled = false;
|
||||
this.forceDisabled = false;
|
||||
this.emptyServerName = false;
|
||||
this.currSettings = {};
|
||||
this.initSettings();
|
||||
|
|
@ -1170,6 +1171,7 @@ class Settings {
|
|||
setMethodUI,
|
||||
forceEnabled,
|
||||
emptyServerName,
|
||||
forceDisabled,
|
||||
) {
|
||||
// Get global needed data
|
||||
this.currAction = action;
|
||||
|
|
@ -1179,6 +1181,7 @@ class Settings {
|
|||
this.setMethodUI = setMethodUI;
|
||||
this.forceEnabled = forceEnabled;
|
||||
this.emptyServerName = emptyServerName;
|
||||
this.forceDisabled = forceDisabled;
|
||||
|
||||
this.updateOperation();
|
||||
this.updateOldNameValue();
|
||||
|
|
@ -1282,17 +1285,27 @@ class Settings {
|
|||
? true
|
||||
: false;
|
||||
|
||||
if (proDisabled) return inp.setAttribute("disabled", "");
|
||||
let inpDisabledState = inp;
|
||||
if (inp.tagName === "SELECT")
|
||||
inpDisabledState = inp.parentElement
|
||||
.querySelector("[data-select-container]")
|
||||
.querySelector("button[data-setting-select]");
|
||||
|
||||
if (this.forceEnabled) return inp.removeAttribute("disabled");
|
||||
if (this.forceDisabled)
|
||||
return inpDisabledState.setAttribute("disabled", "");
|
||||
if (proDisabled)
|
||||
return inpDisabledState.setAttribute("disabled", "");
|
||||
|
||||
if (this.forceEnabled)
|
||||
return inpDisabledState.removeAttribute("disabled");
|
||||
|
||||
if (method === "ui" || method === "default") {
|
||||
inp.removeAttribute("disabled");
|
||||
inpDisabledState.removeAttribute("disabled");
|
||||
} else {
|
||||
inp.setAttribute("disabled", "");
|
||||
inpDisabledState.setAttribute("disabled", "");
|
||||
}
|
||||
|
||||
if (global) inp.removeAttribute("disabled");
|
||||
if (global) inpDisabledState.removeAttribute("disabled");
|
||||
});
|
||||
} catch (err) {}
|
||||
}
|
||||
|
|
@ -1417,7 +1430,8 @@ class SettingsMultiple extends Settings {
|
|||
inps.forEach((inp) => {
|
||||
// case checkbox
|
||||
if (inp.getAttribute("type") === "checkbox") {
|
||||
const defaultVal = inp.getAttribute("data-default") || "";
|
||||
const defaultVal =
|
||||
inp.getAttribute("data-default-value") || "";
|
||||
|
||||
if (defaultVal === "yes" && !inp.checked) {
|
||||
inp.click();
|
||||
|
|
@ -1426,7 +1440,8 @@ class SettingsMultiple extends Settings {
|
|||
|
||||
// case regular
|
||||
if (inp.getAttribute("type") !== "checkbox") {
|
||||
const defaultVal = inp.getAttribute("data-default") || "";
|
||||
const defaultVal =
|
||||
inp.getAttribute("data-default-value") || "";
|
||||
inp.setAttribute("value", defaultVal);
|
||||
inp.value = defaultVal;
|
||||
}
|
||||
|
|
@ -1438,7 +1453,8 @@ class SettingsMultiple extends Settings {
|
|||
"button[data-setting-select]",
|
||||
);
|
||||
selects.forEach((select) => {
|
||||
const defaultVal = select.getAttribute("data-default") || "";
|
||||
const defaultVal =
|
||||
select.getAttribute("data-default-value") || "";
|
||||
select
|
||||
.querySelector("data-setting-select-text")
|
||||
.setAttribute("data-value", defaultVal);
|
||||
|
|
@ -1467,7 +1483,10 @@ class SettingsMultiple extends Settings {
|
|||
? true
|
||||
: false;
|
||||
|
||||
return proDisabled;
|
||||
const isReadOnly = this.forceDisabled;
|
||||
if (proDisabled || isReadOnly) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
removePrevMultiples() {
|
||||
|
|
@ -1958,6 +1977,7 @@ class SettingsAdvanced extends SettingsEditor {
|
|||
setMethodUI = false,
|
||||
forceEnabled = false,
|
||||
emptyServerName = false,
|
||||
forceDisabled = false,
|
||||
) {
|
||||
this.updateData(
|
||||
action,
|
||||
|
|
@ -1967,6 +1987,7 @@ class SettingsAdvanced extends SettingsEditor {
|
|||
setMethodUI,
|
||||
forceEnabled,
|
||||
emptyServerName,
|
||||
forceDisabled,
|
||||
);
|
||||
this.setSettingsAdvanced();
|
||||
this.resetServerName();
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -47,6 +47,9 @@
|
|||
@apply tracking-wide dark:brightness-90 inline-block px-4 py-2 md:px-5 md:py-2.5 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-sky-500 hover:bg-sky-500/80 focus:bg-sky-500/80 leading-normal ease-in shadow-xs hover:-translate-y-px active:opacity-85 hover:shadow-md disabled:cursor-not-allowed dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400/0 dark:disabled:bg-gray-700 dark:disabled:border-gray-700/0 disabled:hover:translate-y-0 disabled:hover:bg-gray-400 disabled:hover:border-gray-400/0 dark:disabled:hover:translate-y-0 dark:disabled:hover:bg-gray-700 dark:disabled:hover:border-gray-700/0;
|
||||
}
|
||||
|
||||
.btn-disabled-style {
|
||||
@apply disabled:cursor-not-allowed dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400/0 dark:disabled:bg-gray-700 dark:disabled:border-gray-700/0 disabled:hover:translate-y-0 disabled:hover:bg-gray-400 disabled:hover:border-gray-400/0 dark:disabled:hover:translate-y-0 dark:disabled:hover:bg-gray-700 dark:disabled:hover:border-gray-700/0;
|
||||
}
|
||||
/*----------------------------------------------*/
|
||||
/*---------------SETTINGS_PLUGINS---------------*/
|
||||
/*----------------------------------------------*/
|
||||
|
|
@ -663,13 +666,17 @@
|
|||
}
|
||||
|
||||
.plugins-list-items-delete {
|
||||
@apply z-20 mx-2 inline-block font-bold text-left text-white uppercase align-middle transition-all cursor-pointer text-xs ease-in tracking-tight-rem hover:-translate-y-px;
|
||||
@apply z-20 mx-2 inline-block font-bold text-left text-white uppercase align-middle transition-all cursor-pointer text-xs ease-in tracking-tight-rem hover:-translate-y-px disabled:cursor-not-allowed;
|
||||
}
|
||||
|
||||
.plugins-list-items-delete-svg {
|
||||
@apply h-5 w-5 fill-red-500 dark:brightness-90;
|
||||
}
|
||||
|
||||
.readonly.plugins-list-items-delete-svg {
|
||||
@apply cursor-not-allowed opacity-[0.5];
|
||||
}
|
||||
|
||||
.plugins-list-items-link {
|
||||
@apply hover:-translate-y-px mx-1;
|
||||
}
|
||||
|
|
|
|||
26
src/ui/templates/account.html
vendored
26
src/ui/templates/account.html
vendored
|
|
@ -142,7 +142,7 @@
|
|||
class="flex flex-col relative col-span-12 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full">
|
||||
<h5 class="input-title">License key</h5>
|
||||
<label class="sr-only" for="license">License key</label>
|
||||
<input type="password"
|
||||
<input {% if is_readonly%}disabled{% endif %} type="password"
|
||||
id="license"
|
||||
name="license"
|
||||
class="col-span-12 regular-input"
|
||||
|
|
@ -173,7 +173,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="col-span-12 flex justify-center mt-6">
|
||||
<button type="submit"
|
||||
<button {% if is_readonly%}disabled{% endif %} type="submit"
|
||||
id="activate-key-button"
|
||||
name="activate-key-button"
|
||||
class="valid-btn">SAVE</button>
|
||||
|
|
@ -200,7 +200,7 @@
|
|||
<div class="flex flex-col relative col-span-12 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full">
|
||||
<h5 class="input-title">Username</h5>
|
||||
<label class="sr-only" for="admin_username">New username</label>
|
||||
<input type="text"
|
||||
<input {% if is_readonly%}disabled{% endif %} type="text"
|
||||
id="admin_username"
|
||||
name="admin_username"
|
||||
class="col-span-12 regular-input"
|
||||
|
|
@ -216,7 +216,7 @@
|
|||
class="flex flex-col relative col-span-12 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full">
|
||||
<h5 class="input-title">Password</h5>
|
||||
<label class="sr-only" for="curr_password">Password</label>
|
||||
<input type="password"
|
||||
<input {% if is_readonly%}disabled{% endif %} type="password"
|
||||
id="curr_password"
|
||||
name="curr_password"
|
||||
class="col-span-12 regular-input"
|
||||
|
|
@ -249,7 +249,7 @@
|
|||
</div>
|
||||
<!-- end password inpt-->
|
||||
<div class="col-span-12 flex justify-center mt-6">
|
||||
<button type="submit"
|
||||
<button {% if is_readonly%}disabled{% endif %} type="submit"
|
||||
id="username-button"
|
||||
name="username-button"
|
||||
value="username"
|
||||
|
|
@ -277,7 +277,7 @@
|
|||
class="flex flex-col relative col-span-12 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full">
|
||||
<h5 class="input-title">Password</h5>
|
||||
<label class="sr-only" for="curr_password">Password</label>
|
||||
<input type="password"
|
||||
<input {% if is_readonly%}disabled{% endif %} type="password"
|
||||
id="curr_password"
|
||||
name="curr_password"
|
||||
class="col-span-12 regular-input"
|
||||
|
|
@ -314,7 +314,7 @@
|
|||
class="flex flex-col relative col-span-12 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full">
|
||||
<h5 class="input-title">New password</h5>
|
||||
<label class="sr-only" for="admin_password">New password</label>
|
||||
<input type="password"
|
||||
<input {% if is_readonly%}disabled{% endif %} type="password"
|
||||
id="admin_password"
|
||||
name="admin_password"
|
||||
class="col-span-12 regular-input"
|
||||
|
|
@ -351,7 +351,7 @@
|
|||
class="flex flex-col relative col-span-12 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full">
|
||||
<h5 class="input-title">Confirm new password</h5>
|
||||
<label class="sr-only" for="admin_password_check">Confirm new password</label>
|
||||
<input type="password"
|
||||
<input {% if is_readonly%}disabled{% endif %} type="password"
|
||||
id="admin_password_check"
|
||||
name="admin_password_check"
|
||||
class="col-span-12 regular-input"
|
||||
|
|
@ -384,7 +384,7 @@
|
|||
<strong class="opacity-0 font-normal text-sm text-red-500" data-pw-alert>Value does not match password</strong>
|
||||
</div>
|
||||
<div class="col-span-12 flex justify-center">
|
||||
<button type="submit" id="pw-button" name="pw-button" class="valid-btn">Save</button>
|
||||
<button {% if is_readonly%}disabled{% endif %} type="submit" id="pw-button" name="pw-button" class="valid-btn">Save</button>
|
||||
</div>
|
||||
</form>
|
||||
<form data-tab-item="totp"
|
||||
|
|
@ -426,7 +426,7 @@
|
|||
class="flex flex-col relative col-span-12 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full">
|
||||
<h5 class="input-title">Secret token</h5>
|
||||
<label class="sr-only" for="secret_token">secret token</label>
|
||||
<input type="password"
|
||||
<input {% if is_readonly%}disabled{% endif %} type="password"
|
||||
id="secret_token"
|
||||
name="secret_token"
|
||||
class="col-span-12 regular-input"
|
||||
|
|
@ -477,7 +477,7 @@
|
|||
<div class="flex flex-col relative col-span-12 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full">
|
||||
<h5 class="input-title">2FA code</h5>
|
||||
<label class="sr-only" for="totp_token">totp code</label>
|
||||
<input type="text"
|
||||
<input {% if is_readonly%}disabled{% endif %} type="text"
|
||||
id="totp_token"
|
||||
name="totp_token"
|
||||
class="col-span-12 regular-input"
|
||||
|
|
@ -492,7 +492,7 @@
|
|||
class="flex flex-col relative col-span-12 px-4 my-2 md:px-6 md:my-3 lg:px-6 lg:my-3 max-w-[400px] w-full">
|
||||
<h5 class="input-title">Password</h5>
|
||||
<label class="sr-only" for="curr_password">Password</label>
|
||||
<input type="password"
|
||||
<input {% if is_readonly%}disabled{% endif %} type="password"
|
||||
id="curr_password"
|
||||
name="curr_password"
|
||||
class="col-span-12 regular-input"
|
||||
|
|
@ -525,7 +525,7 @@
|
|||
</div>
|
||||
<!-- end password inpt-->
|
||||
<div class="col-span-12 flex justify-center mt-6">
|
||||
<button type="submit"
|
||||
<button {% if is_readonly%}disabled{% endif %} type="submit"
|
||||
id="totp-button"
|
||||
name="totp-button"
|
||||
value="totp"
|
||||
|
|
|
|||
8
src/ui/templates/bans.html
vendored
8
src/ui/templates/bans.html
vendored
|
|
@ -14,9 +14,9 @@
|
|||
|
||||
<!-- actions -->
|
||||
<div class="col-span-12 relative flex justify-center min-w-0 break-words rounded-2xl bg-clip-border">
|
||||
<button data-add-ban
|
||||
<button {% if is_readonly%}disabled{% endif %} data-add-ban
|
||||
type="button"
|
||||
class="dark:bg-green-500/90 duration-300 w-80 flex justify-center items-center px-6 py-3 font-bold text-center text-white dark:text-gray-200 uppercase align-middle transition-all rounded-lg cursor-pointer bg-green-500 hover:bg-green-500/80 focus:bg-green-500/80 leading-normal text-base ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md">
|
||||
class="disabled:cursor-not-allowed dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400/0 dark:disabled:bg-gray-700 dark:disabled:border-gray-700/0 disabled:hover:translate-y-0 disabled:hover:bg-gray-400 disabled:hover:border-gray-400/0 dark:disabled:hover:translate-y-0 dark:disabled:hover:bg-gray-700 dark:disabled:hover:border-gray-700/0 dark:bg-green-500/90 duration-300 dark:text-gray-100 w-80 flex justify-center items-center px-6 py-3 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-green-500 hover:bg-green-500/80 focus:bg-green-500/80 leading-normal text-base ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md">
|
||||
<span class="mr-2">Add ban</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
|
|
@ -139,7 +139,7 @@
|
|||
data-checkbox-handler="ban-item-{{ loop.index }}"
|
||||
class="relative mb-7 md:mb-0 z-10 ml-2">
|
||||
<label class="sr-only" for="ban-item-{{ loop.index }}">Ban ip {{ loop.index }}</label>
|
||||
<input id="ban-item-{{ loop.index }}"
|
||||
<input {% if is_readonly%}disabled{% endif %} id="ban-item-{{ loop.index }}"
|
||||
name="ban-item-{{ loop.index }}"
|
||||
data-default-method="ui"
|
||||
data-default-value="no"
|
||||
|
|
@ -182,7 +182,7 @@
|
|||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="operation" value="unban">
|
||||
<input data-unban-inp type="hidden" name="data" value="">
|
||||
<button data-unban-btn
|
||||
<button {% if is_readonly%}disabled{% endif %} data-unban-btn
|
||||
disabled
|
||||
type="submit"
|
||||
class="valid-btn mr-3 text-base">UNBAN</button>
|
||||
|
|
|
|||
1
src/ui/templates/base.html
vendored
1
src/ui/templates/base.html
vendored
|
|
@ -22,6 +22,7 @@
|
|||
<!-- info -->
|
||||
<main class="xl:pl-75 w-full px-2 sm:px-6 pb-0 pt-20 sm:pt-3 min-h-[85vh] flex flex-col justify-between">
|
||||
<div class="max-w-[1920px] grid gap-y-4 gap-3 sm:gap-4 lg:gap-6 grid-cols-12 w-full">
|
||||
<div class="hidden" data-global-is-readonly="{%if is_readonly%}true{% else%}false{%endif%}"></div>
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
{% include "footer.html" %}
|
||||
|
|
|
|||
2
src/ui/templates/file_manager.html
vendored
2
src/ui/templates/file_manager.html
vendored
|
|
@ -1,6 +1,6 @@
|
|||
{% set current_endpoint = url_for(request.endpoint)[1:].split("/")[-1].strip().replace('_', '-') %}
|
||||
<!-- main container -->
|
||||
<div data-{{ current_endpoint }}-container class="min-h-[400px] flex flex-col justify-between dark:brightness-110 md:min-h-50-screen col-span-12 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
|
||||
<div data-readonly="{% if is_readonly %}true{% else %}false{% endif %}" data-{{ current_endpoint }}-container class="min-h-[400px] flex flex-col justify-between dark:brightness-110 md:min-h-50-screen col-span-12 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
|
||||
<div class="mb-4 px-3">
|
||||
<div class="w-full grid-cols-12 grid">
|
||||
<div class="col-span-12 md:col-span-8">
|
||||
|
|
|
|||
2
src/ui/templates/global_config.html
vendored
2
src/ui/templates/global_config.html
vendored
|
|
@ -64,7 +64,7 @@
|
|||
<!-- end plugin item -->
|
||||
<!-- submit -->
|
||||
<div class="flex w-full justify-center mt-8 mb-2">
|
||||
<button type="submit" class="valid-btn">SAVE</button>
|
||||
<button {% if is_readonly%}disabled{% endif %} type="submit" class="valid-btn">SAVE</button>
|
||||
</div>
|
||||
<!-- end submit -->
|
||||
</form>
|
||||
|
|
|
|||
2
src/ui/templates/jobs.html
vendored
2
src/ui/templates/jobs.html
vendored
|
|
@ -147,7 +147,7 @@
|
|||
<div class="{{ data['custom_class'] }} relative dark:text-gray-400 text-sm m-0 my-1 mr-1"
|
||||
data-{{attribute_name}}-files>
|
||||
{% if value['cache'] %}
|
||||
<button data-{{attribute_name}}-setting-select="{{ job_name }}"
|
||||
<button {% if is_readonly%}disabled{% endif %} data-{{attribute_name}}-setting-select="{{ job_name }}"
|
||||
class="py-1 text-sm disabled:opacity-75 dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400 dark:disabled:bg-gray-800 dark:disabled:border-gray-800 duration-300 ease-in-out dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 focus:border-green-500 flex justify-between align-middle items-center text-left leading-6 ease w-full rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-1.5 md:px-3 font-normal text-gray-700 transition-all placeholder:text-gray-500">
|
||||
<span id="jobs-{{ job_name }}"
|
||||
data-name="jobs-{{ job_name }}"
|
||||
|
|
|
|||
2
src/ui/templates/login.html
vendored
2
src/ui/templates/login.html
vendored
|
|
@ -204,7 +204,7 @@
|
|||
renderer: 'svg',
|
||||
loop: true,
|
||||
autoplay: true,
|
||||
path: "json/PARTICLES.json" // the path to the animation json
|
||||
path: "json/particles.json" // the path to the animation json
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
|
|
|||
9
src/ui/templates/plugins.html
vendored
9
src/ui/templates/plugins.html
vendored
|
|
@ -13,6 +13,7 @@
|
|||
] %}
|
||||
{% include "card_info.html" %}
|
||||
|
||||
{% if not is_readonly %}
|
||||
<!-- upload layout -->
|
||||
<div data-{{attribute_name}}-upload
|
||||
class="p-4 col-span-12 md:col-span-7 2xl:col-span-4 grid grid-cols-12 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
|
||||
|
|
@ -49,6 +50,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<!-- end upload layout -->
|
||||
{% endif %}
|
||||
|
||||
<!-- filter -->
|
||||
{% set filters = [
|
||||
{
|
||||
|
|
@ -103,12 +106,12 @@
|
|||
</svg>
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if plugin['type'] == "external" %}
|
||||
<button data-{{attribute_name}}-action="delete"
|
||||
{% if plugin['type'] == "external" and plugin['method'] in ('ui', 'manual') %}
|
||||
<button {% if is_readonly%}disabled{% endif %} data-{{attribute_name}}-action="delete"
|
||||
name="{{ plugin['id'] }}"
|
||||
aria-label="delete plugin"
|
||||
class="plugins-list-items-delete">
|
||||
<svg class="plugins-list-items-delete-svg"
|
||||
<svg class="plugins-list-items-delete-svg {% if is_readonly%}readonly{% endif %}"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 448 512">
|
||||
<path d="M135.2 17.7L128 32H32C14.3 32 0 46.3 0 64S14.3 96 32 96H416c17.7 0 32-14.3 32-32s-14.3-32-32-32H320l-7.2-14.3C307.4 6.8 296.3 0 284.2 0H163.8c-12.1 0-23.2 6.8-28.6 17.7zM416 128H32L53.2 467c1.6 25.3 22.6 45 47.9 45H346.9c25.3 0 46.3-19.7 47.9-45L416 128z" />
|
||||
|
|
|
|||
8
src/ui/templates/services.html
vendored
8
src/ui/templates/services.html
vendored
|
|
@ -31,13 +31,13 @@
|
|||
class="col-span-12 relative flex justify-center min-w-0 break-words rounded-2xl bg-clip-border">
|
||||
<div data-is-draft class="hidden" data-value="no"></div>
|
||||
<div data-service-method class="hidden" data-value="ui"></div>
|
||||
<button data-{{attribute_name}}-action="new"
|
||||
<button {% if is_readonly%}disabled{% endif %} data-{{attribute_name}}-action="new"
|
||||
data-{{attribute_name}}-name="service"
|
||||
data-old-name
|
||||
data-value="new"
|
||||
data-settings="{}"
|
||||
type="button"
|
||||
class="dark:bg-green-500/90 duration-300 dark:text-gray-100 w-80 flex justify-center items-center px-6 py-3 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-green-500 hover:bg-green-500/80 focus:bg-green-500/80 leading-normal text-base ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md">
|
||||
class="disabled:cursor-not-allowed dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400/0 dark:disabled:bg-gray-700 dark:disabled:border-gray-700/0 disabled:hover:translate-y-0 disabled:hover:bg-gray-400 disabled:hover:border-gray-400/0 dark:disabled:hover:translate-y-0 dark:disabled:hover:bg-gray-700 dark:disabled:hover:border-gray-700/0 dark:bg-green-500/90 duration-300 dark:text-gray-100 w-80 flex justify-center items-center px-6 py-3 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-green-500 hover:bg-green-500/80 focus:bg-green-500/80 leading-normal text-base ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md">
|
||||
<span class="mr-2">new service</span>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
|
|
@ -284,8 +284,10 @@
|
|||
{% endif %}
|
||||
{% for button in action_buttons %}
|
||||
<button
|
||||
{% if button['name'] == "clone" and is_readonly%}disabled{% endif %}
|
||||
{% if button['name'] == "clone" or button['name'] == "edit"%}
|
||||
data-settings="{{ service['settings'] }}"
|
||||
|
||||
{% endif %}
|
||||
{% if button['name'] == "new"%}
|
||||
data-settings="{}"
|
||||
|
|
@ -293,7 +295,7 @@
|
|||
data-{{attribute_name}}-action="{{ button['name'] }}"
|
||||
aria-label="{{ button['label'] }}"
|
||||
data-{{attribute_name}}-name="{{ service['SERVER_NAME']['value'] }}"
|
||||
class="dark:brightness-90 z-20 mx-1 bg-{{ button['color'] }} hover:bg-{{ button['color'] }}/80 focus:bg-{{ button['color'] }}/80 inline-block p-3 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer leading-normal text-xs ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 active:opacity-85 hover:shadow-md">
|
||||
class="disabled:cursor-not-allowed dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400/0 dark:disabled:bg-gray-700 dark:disabled:border-gray-700/0 disabled:hover:translate-y-0 disabled:hover:bg-gray-400 disabled:hover:border-gray-400/0 dark:disabled:hover:translate-y-0 dark:disabled:hover:bg-gray-700 dark:disabled:hover:border-gray-700/0 dark:brightness-90 z-20 mx-1 bg-{{ button['color'] }} hover:bg-{{ button['color'] }}/80 focus:bg-{{ button['color'] }}/80 inline-block p-3 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer leading-normal text-xs ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 active:opacity-85 hover:shadow-md">
|
||||
{% if button['name'] == "clone" %}
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
|
|
|
|||
2
src/ui/templates/services_modal.html
vendored
2
src/ui/templates/services_modal.html
vendored
|
|
@ -1,5 +1,5 @@
|
|||
<!-- modal -->
|
||||
<div data-services-plugins-container
|
||||
<div data-readonly="{% if is_readonly %}true{% else %}false{% endif %}" data-services-plugins-container
|
||||
data-services-modal
|
||||
class="dark:brightness-110 hidden w-screen h-screen fixed bg-gray-600/50 z-[1001] top-0 left-0 justify-center items-center">
|
||||
<div data-services-modal-card
|
||||
|
|
|
|||
2
src/ui/templates/setting_checkbox.html
vendored
2
src/ui/templates/setting_checkbox.html
vendored
|
|
@ -13,7 +13,7 @@
|
|||
<div data-checkbox-handler="{{ inp_id }}"
|
||||
class="relative mb-7 md:mb-0 z-10 ">
|
||||
<label class="sr-only" for="{{ inp_name_mult }}">{{ inp_name }}</label>
|
||||
<input id="{{ inp_name_mult }}"
|
||||
<input {% if is_readonly%}disabled{% endif %} id="{{ inp_name_mult }}"
|
||||
name="{{ inp_name_mult }}"
|
||||
data-default-method="{% if inp_name in ['AUTOCONF_MODE', 'SWARM_MODE', 'KUBERNETES_MODE'] %}mode{% else %}{{ global_config_method }}{% endif %}"
|
||||
data-default-value="{{ global_config[inp_name]['value'] }}"
|
||||
|
|
|
|||
2
src/ui/templates/setting_input.html
vendored
2
src/ui/templates/setting_input.html
vendored
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
<div class="relative flex items-center">
|
||||
<label class="sr-only" for="{{ inp_name_mult }}">{{ inp_name }}</label>
|
||||
<input {% if inp_name == "SERVER_NAME" %}required{% endif %}
|
||||
<input {% if is_readonly%}disabled{% endif %} {% if inp_name == "SERVER_NAME" %}required{% endif %}
|
||||
data-default-value="{{ global_config_value }}"
|
||||
data-default-method="{{ global_config_method }}"
|
||||
data-setting-input
|
||||
|
|
|
|||
2
src/ui/templates/setting_select.html
vendored
2
src/ui/templates/setting_select.html
vendored
|
|
@ -33,7 +33,7 @@
|
|||
<!-- end default hidden-->
|
||||
<!--custom-->
|
||||
<div data-select-container class="relative">
|
||||
<button {% if global_config_method != 'ui' and global_config_method != 'default' or is_read_only %}disabled{% endif %}
|
||||
<button {% if is_readonly%}disabled{% endif %} {% if global_config_method != 'ui' and global_config_method != 'default' or is_read_only %}disabled{% endif %}
|
||||
data-setting-select="{{ inp_id }}"
|
||||
data-default-value="{{ global_config_value }}"
|
||||
data-default-method="{{ global_config_method }}"
|
||||
|
|
|
|||
11
src/ui/templates/setup.html
vendored
11
src/ui/templates/setup.html
vendored
|
|
@ -180,6 +180,7 @@
|
|||
{% else %}
|
||||
<h6 class="col-span-12 block text-left font-bold mb-4 mt-2">🧑🚀 An admin user already exists</h6>
|
||||
{% endif %}
|
||||
<h2 class="col-span-12 block text-left font-bold my-4 text-2xl">Newsletter</h2>
|
||||
<!-- email inpt-->
|
||||
<div class="flex flex-col relative col-span-12 mx-2 max-w-[400px] w-full">
|
||||
<h5 class="text-base mb-1 transition duration-300 ease-in-out text-md font-bold m-0">Email</h5>
|
||||
|
|
@ -536,6 +537,7 @@
|
|||
this.servInp = document.querySelector("#server_name");
|
||||
this.sslCheck = document.querySelector("#auto_lets_encrypt");
|
||||
this.urlInp = document.querySelector("#ui_url");
|
||||
this.newsForm = document.querySelector("#newsletter-form");
|
||||
this.emailInp = document.querySelector("#newsletter-email")
|
||||
this.checkEmailInp = document.querySelector("#newsletter-check");
|
||||
this.loaderContainer = document.querySelector("[data-loader]");
|
||||
|
|
@ -572,6 +574,11 @@
|
|||
} catch (err) {}
|
||||
});
|
||||
|
||||
// Avoid reload on newsletter submit
|
||||
this.newsForm.addEventListener('submit', (e) => {
|
||||
e.preventDefault()
|
||||
})
|
||||
|
||||
// Submit
|
||||
this.formEl.addEventListener("submit", (e) => {
|
||||
{% if not ui_user %}
|
||||
|
|
@ -587,7 +594,7 @@
|
|||
this.hideErrMsg();
|
||||
|
||||
// Send email
|
||||
if(this.checkEmailInp.checked) {
|
||||
if(this.checkEmailInp.checked && this.checkEmailInp.checkValidity()) {
|
||||
this.subscribe();
|
||||
}
|
||||
|
||||
|
|
@ -644,7 +651,7 @@
|
|||
|
||||
subscribe() {
|
||||
document.querySelector('#newsletter-form-email').value = this.emailInp.value;
|
||||
document.querySelector('#newsletter-form').submit();
|
||||
this.newsForm.submit();
|
||||
}
|
||||
|
||||
{% if not ui_user %}
|
||||
|
|
|
|||
16
src/ui/templates/totp.html
vendored
16
src/ui/templates/totp.html
vendored
|
|
@ -10,6 +10,8 @@
|
|||
<link rel="stylesheet" href="css/dashboard.css" />
|
||||
<link rel="stylesheet" href="css/login.css" />
|
||||
<script defer src="./js/totp.js" nonce="{{ script_nonce }}"></script>
|
||||
<script src="js/lottie-web.min.js" defer nonce="{{ script_nonce }}"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div data-loader
|
||||
|
|
@ -108,10 +110,9 @@
|
|||
<!-- end form -->
|
||||
<!-- particles -->
|
||||
<div class="-z-10 fixed bg-primary">
|
||||
<div id="particles-js" class="login-img [&>*]:bg-primary"></div>
|
||||
<div id="lottie-particles" data-lottie="/lotties/PARTICLES.json" class="fixed top-0 left-0 w-full h-full login-img [&>*]:bg-primary"></div>
|
||||
</div>
|
||||
</main>
|
||||
<script src="js/tsparticles.bundle.min.js" nonce="{{ script_nonce }}"></script>
|
||||
<script nonce="{{ script_nonce }}">
|
||||
class Loader {
|
||||
constructor() {
|
||||
|
|
@ -187,7 +188,16 @@
|
|||
|
||||
const setLoader = new Loader();
|
||||
const setFlash = new FlashMsg();
|
||||
tsParticles.loadJSON("particles-js", "json/particles.json");
|
||||
|
||||
window.addEventListener("load", () => {
|
||||
lottie.loadAnimation({
|
||||
container: document.querySelector("#lottie-particles"), // the dom element that will contain the animation
|
||||
renderer: 'svg',
|
||||
loop: true,
|
||||
autoplay: true,
|
||||
path: "json/particles.json" // the path to the animation json
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
requests==2.31.0
|
||||
requests==2.32.2
|
||||
selenium<4.17.0
|
||||
|
|
|
|||
|
|
@ -133,9 +133,9 @@ pysocks==1.7.1 \
|
|||
--hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \
|
||||
--hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0
|
||||
# via urllib3
|
||||
requests==2.31.0 \
|
||||
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
|
||||
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
|
||||
requests==2.32.2 \
|
||||
--hash=sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289 \
|
||||
--hash=sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c
|
||||
# via -r requirements.in
|
||||
selenium==4.16.0 \
|
||||
--hash=sha256:aec71f4e6ed6cb3ec25c9c1b5ed56ae31b6da0a7f17474c7566d303f84e6219f \
|
||||
|
|
@ -149,9 +149,9 @@ sortedcontainers==2.4.0 \
|
|||
--hash=sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88 \
|
||||
--hash=sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0
|
||||
# via trio
|
||||
trio==0.25.0 \
|
||||
--hash=sha256:9b41f5993ad2c0e5f62d0acca320ec657fdb6b2a2c22b8c7aed6caf154475c4e \
|
||||
--hash=sha256:e6458efe29cc543e557a91e614e2b51710eba2961669329ce9c862d50c6e8e81
|
||||
trio==0.25.1 \
|
||||
--hash=sha256:9f5314f014ea3af489e77b001861c535005c3858d38ec46b6b071ebfa339d7fb \
|
||||
--hash=sha256:e42617ba091e7b2e50c899052e83a3c403101841de925187f61e7b7eaebdf3fb
|
||||
# via
|
||||
# selenium
|
||||
# trio-websocket
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
requests==2.31.0
|
||||
requests==2.32.2
|
||||
selenium<4.17.0
|
||||
|
|
|
|||
|
|
@ -133,9 +133,9 @@ pysocks==1.7.1 \
|
|||
--hash=sha256:2725bd0a9925919b9b51739eea5f9e2bae91e83288108a9ad338b2e3a4435ee5 \
|
||||
--hash=sha256:3f8804571ebe159c380ac6de37643bb4685970655d3bba243530d6558b799aa0
|
||||
# via urllib3
|
||||
requests==2.31.0 \
|
||||
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \
|
||||
--hash=sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1
|
||||
requests==2.32.2 \
|
||||
--hash=sha256:dd951ff5ecf3e3b3aa26b40703ba77495dab41da839ae72ef3c8e5d8e2433289 \
|
||||
--hash=sha256:fc06670dd0ed212426dfeb94fc1b983d917c4f9847c863f313c9dfaaffb7c23c
|
||||
# via -r requirements.in
|
||||
selenium==4.16.0 \
|
||||
--hash=sha256:aec71f4e6ed6cb3ec25c9c1b5ed56ae31b6da0a7f17474c7566d303f84e6219f \
|
||||
|
|
@ -149,9 +149,9 @@ sortedcontainers==2.4.0 \
|
|||
--hash=sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88 \
|
||||
--hash=sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0
|
||||
# via trio
|
||||
trio==0.25.0 \
|
||||
--hash=sha256:9b41f5993ad2c0e5f62d0acca320ec657fdb6b2a2c22b8c7aed6caf154475c4e \
|
||||
--hash=sha256:e6458efe29cc543e557a91e614e2b51710eba2961669329ce9c862d50c6e8e81
|
||||
trio==0.25.1 \
|
||||
--hash=sha256:9f5314f014ea3af489e77b001861c535005c3858d38ec46b6b071ebfa339d7fb \
|
||||
--hash=sha256:e42617ba091e7b2e50c899052e83a3c403101841de925187f61e7b7eaebdf3fb
|
||||
# via
|
||||
# selenium
|
||||
# trio-websocket
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue