Merge branch 'dev' into staging

This commit is contained in:
florian 2024-04-19 16:43:45 +02:00
commit 05db940e05
No known key found for this signature in database
GPG key ID: 93EE47CC3D061500
106 changed files with 1890 additions and 1109 deletions

View file

@ -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@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0
uses: github/codeql-action/init@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
with:
languages: ${{ matrix.language }}
config-file: ./.github/codeql.yml
setup-python-dependencies: false
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0
uses: github/codeql-action/analyze@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
with:
category: "/language:${{matrix.language}}"

View file

@ -32,7 +32,7 @@ jobs:
run: mkdocs serve & sleep 10
- name: Run pdf script
run: node docs/misc/pdf.js http://localhost:8000/print_page/ BunkerWeb_documentation_v${{ inputs.VERSION }}.pdf 'BunkerWeb documentation v${{ inputs.VERSION }}'
- uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
- uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2
with:
name: BunkerWeb_documentation_v${{ inputs.VERSION }}.pdf
path: BunkerWeb_documentation_v${{ inputs.VERSION }}.pdf

View file

@ -129,7 +129,7 @@ jobs:
scp -r root@arm:/root/package-${{ inputs.LINUX }} ./package-${{ inputs.LINUX }}
env:
LARCH: ${{ env.LARCH }}
- uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
- uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2
with:
name: package-${{ inputs.LINUX }}-${{ env.LARCH }}
path: package-${{ inputs.LINUX }}/*.${{ inputs.PACKAGE }}

View file

@ -19,7 +19,7 @@ jobs:
# Get PDF doc
- name: Get documentation
if: inputs.VERSION != 'testing'
uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
uses: actions/download-artifact@8caf195ad4b1dee92908e23f56eeb0696f1dd42d # v4.1.5
with:
name: BunkerWeb_documentation_v${{ inputs.VERSION }}.pdf
# Create tag

View file

@ -42,18 +42,18 @@ jobs:
- name: Check out repository code
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- name: Install ruby
uses: ruby/setup-ruby@5f19ec79cedfadb78ab837f95b87734d0003c899 # v1.173.0
uses: ruby/setup-ruby@6bd3d993c602f6b675728ebaecb2b569ff86e99b # v1.174.0
with:
ruby-version: "3.0"
- name: Install packagecloud
run: gem install package_cloud
# Download packages
- uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
- uses: actions/download-artifact@8caf195ad4b1dee92908e23f56eeb0696f1dd42d # v4.1.5
if: inputs.LINUX != 'el' && inputs.LINUX != 'el9'
with:
name: package-${{ inputs.LINUX }}-${{ inputs.PACKAGE_ARCH }}
path: /tmp/${{ inputs.LINUX }}
- uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
- uses: actions/download-artifact@8caf195ad4b1dee92908e23f56eeb0696f1dd42d # v4.1.5
if: inputs.LINUX == 'el' || inputs.LINUX == 'el9'
with:
name: package-rh${{ inputs.LINUX }}-${{ inputs.PACKAGE_ARCH }}

View file

@ -25,6 +25,6 @@ jobs:
results_format: sarif
publish_results: true
- name: "Upload SARIF results to code scanning"
uses: github/codeql-action/upload-sarif@df5a14dc28094dc936e103b37d749c6628682b60 # v3.25.0
uses: github/codeql-action/upload-sarif@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
with:
sarif_file: results.sarif

View file

@ -52,7 +52,7 @@ jobs:
if: always()
env:
SECRET_KEY: ${{ secrets.SECRET_KEY }}
- uses: actions/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1
- uses: actions/upload-artifact@1746f4ab65b179e0ea60a494b83293b640dd5bba # v4.3.2
if: always()
with:
name: tf-${{ inputs.TYPE }}

View file

@ -23,7 +23,7 @@ jobs:
uses: actions/checkout@9bb56186c3b09b4f86b1c65136769dd318469633 # v4.1.2
- name: Install terraform
uses: hashicorp/setup-terraform@a1502cd9e758c50496cc9ac5308c4843bcd56d36 # v3.0.0
- uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
- uses: actions/download-artifact@8caf195ad4b1dee92908e23f56eeb0696f1dd42d # v4.1.5
with:
name: tf-${{ inputs.TYPE }}
path: /tmp

View file

@ -43,7 +43,7 @@ jobs:
if: inputs.TYPE == 'swarm'
- name: Install test dependencies
run: PIP_BREAK_SYSTEM_PACKAGES=1 pip3 install --no-cache-dir --require-hashes --no-deps -r tests/requirements.txt
- uses: actions/download-artifact@c850b930e6ba138125429b7e5c93fc707a7f8427 # v4.1.4
- uses: actions/download-artifact@8caf195ad4b1dee92908e23f56eeb0696f1dd42d # v4.1.5
with:
name: tf-k8s
path: /tmp

View file

@ -9,3 +9,4 @@ src/ui/templates/settings_plugins.html:hashicorp-tf-password:87
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

View file

@ -3,7 +3,7 @@
exclude: (^LICENSE.md$|^src/VERSION$|^env/|^src/(bw/misc/root-ca.pem$|deps/src/|common/core/modsecurity/files|ui/static/(js/(editor/|utils/purify/|tsparticles\.bundle\.min\.js)|css/dashboard\.css))|\.(svg|drawio|patch\d?|ascii|tf|tftpl|key)$)
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: c4a0b883114b00d8d76b479c820ce7950211c99b # frozen: v4.5.0
rev: 2c9f875913ee60ca25ce70243dc24d5b6415598c # frozen: v4.6.0
hooks:
- id: requirements-txt-fixer
name: Fix requirements.txt and requirements.in files
@ -17,7 +17,7 @@ repos:
- id: check-case-conflict
- repo: https://github.com/psf/black
rev: 552baf822992936134cbd31a38f69c8cfe7c0f05 # frozen: 24.3.0
rev: 8fe627072f15ff2e3d380887b92f7868efaf6d05 # frozen: 24.4.0
hooks:
- id: black
name: Black Python Formatter

View file

@ -10,6 +10,17 @@
- [FEATURE] Add backup plugin to backup and restore easily the database
- [FEATURE] Add LETS_ENCRYPT_CLEAR_OLD_CERTS setting to control if old certificates should be removed when generating Let's Encrypt certificates (default is no)
- [FEATURE] Add DISABLE_DEFAULT_SERVER_STRICT_SNI setting to allow/block requests when SNI is unknown or unset (default is no)
- [UI] General : fix tooltip crop because of overflow
- [UI] General : fix select setting crop because of overflow and check if select is out of viewport to determine visible position
- [UI] General : show logs on UI when pre rendering issue
- [UI] Global config : fix script error while fragment relate to a missing plugin
- [UI] Global config / services page : filtering settings now open plugin select to highlight remaining plugin
- [UI] Global config / services page : add combobox on plugin select open to search a plugin quick
- [UI] Global config / services page : add combobox on plugin select open to search a plugin quick
- [UI] Reporting page : fix missing data and add new ones
- [UI] Account page : keep license key form even if pro register to easy update
- [DOCUMENTATION] Add upgrade procedure for 1.5.7+
- [MISC] Support custom bwcli commands using plugins
- [DEPS] Updated LuaJIT version to v2.1-20240314

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View file

@ -979,6 +979,10 @@ To simplify the installation process, Linux package repositories for BunkerWeb a
!!! warning "Testing/dev version"
If you use the `testing` or `dev` version, you will need to add the `force-bad-version` directive to your `/etc/dpkg/dpkg.cfg` file before installing BunkerWeb.
```shell
echo "force-bad-version" | sudo tee -a /etc/dpkg/dpkg.cfg
```
Optional step : if you want to automatically enable the [setup wizard](web-ui.md#setup-wizard) when BunkerWeb is installed, export the following variable :
```shell
@ -1022,6 +1026,10 @@ To simplify the installation process, Linux package repositories for BunkerWeb a
!!! warning "Testing/dev version"
If you use the `testing` or `dev` version, you will need to add the `force-bad-version` directive to your `/etc/dpkg/dpkg.cfg` file before installing BunkerWeb.
```shell
echo "force-bad-version" | sudo tee -a /etc/dpkg/dpkg.cfg
```
Optional step : if you want to automatically enable the [setup wizard](web-ui.md#setup-wizard) when BunkerWeb is installed, export the following variable :
```shell
@ -1110,7 +1118,7 @@ To simplify the installation process, Linux package repositories for BunkerWeb a
And finally install BunkerWeb 1.5.7 :
```shell
dnf install -y epel-release && \
sudo dnf install -y epel-release && \
curl -s https://packagecloud.io/install/repositories/bunkerity/bunkerweb/script.rpm.sh | sudo bash && \
sudo dnf check-update && \
sudo -E dnf install -y bunkerweb-1.5.7

View file

@ -510,100 +510,100 @@ pyyaml-env-tag==0.1 \
--hash=sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb \
--hash=sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069
# via mkdocs
regex==2023.12.25 \
--hash=sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5 \
--hash=sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770 \
--hash=sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc \
--hash=sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105 \
--hash=sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d \
--hash=sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b \
--hash=sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9 \
--hash=sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630 \
--hash=sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6 \
--hash=sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c \
--hash=sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482 \
--hash=sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6 \
--hash=sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a \
--hash=sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80 \
--hash=sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5 \
--hash=sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1 \
--hash=sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f \
--hash=sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf \
--hash=sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb \
--hash=sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2 \
--hash=sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347 \
--hash=sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20 \
--hash=sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060 \
--hash=sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5 \
--hash=sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73 \
--hash=sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f \
--hash=sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d \
--hash=sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3 \
--hash=sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae \
--hash=sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4 \
--hash=sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2 \
--hash=sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457 \
--hash=sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c \
--hash=sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4 \
--hash=sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87 \
--hash=sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0 \
--hash=sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704 \
--hash=sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f \
--hash=sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f \
--hash=sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b \
--hash=sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5 \
--hash=sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923 \
--hash=sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715 \
--hash=sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c \
--hash=sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca \
--hash=sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1 \
--hash=sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756 \
--hash=sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360 \
--hash=sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc \
--hash=sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445 \
--hash=sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e \
--hash=sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4 \
--hash=sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a \
--hash=sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8 \
--hash=sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53 \
--hash=sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697 \
--hash=sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf \
--hash=sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a \
--hash=sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415 \
--hash=sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f \
--hash=sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9 \
--hash=sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400 \
--hash=sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d \
--hash=sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392 \
--hash=sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb \
--hash=sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd \
--hash=sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861 \
--hash=sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232 \
--hash=sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95 \
--hash=sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7 \
--hash=sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39 \
--hash=sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887 \
--hash=sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5 \
--hash=sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39 \
--hash=sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb \
--hash=sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586 \
--hash=sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97 \
--hash=sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423 \
--hash=sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69 \
--hash=sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7 \
--hash=sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1 \
--hash=sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7 \
--hash=sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5 \
--hash=sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8 \
--hash=sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91 \
--hash=sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590 \
--hash=sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe \
--hash=sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c \
--hash=sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64 \
--hash=sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd \
--hash=sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa \
--hash=sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31 \
--hash=sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988
regex==2024.4.16 \
--hash=sha256:00169caa125f35d1bca6045d65a662af0202704489fada95346cfa092ec23f39 \
--hash=sha256:03576e3a423d19dda13e55598f0fd507b5d660d42c51b02df4e0d97824fdcae3 \
--hash=sha256:03e68f44340528111067cecf12721c3df4811c67268b897fbe695c95f860ac42 \
--hash=sha256:0534b034fba6101611968fae8e856c1698da97ce2efb5c2b895fc8b9e23a5834 \
--hash=sha256:08dea89f859c3df48a440dbdcd7b7155bc675f2fa2ec8c521d02dc69e877db70 \
--hash=sha256:0a38d151e2cdd66d16dab550c22f9521ba79761423b87c01dae0a6e9add79c0d \
--hash=sha256:0c8290b44d8b0af4e77048646c10c6e3aa583c1ca67f3b5ffb6e06cf0c6f0f89 \
--hash=sha256:10188fe732dec829c7acca7422cdd1bf57d853c7199d5a9e96bb4d40db239c73 \
--hash=sha256:1210365faba7c2150451eb78ec5687871c796b0f1fa701bfd2a4a25420482d26 \
--hash=sha256:12f6a3f2f58bb7344751919a1876ee1b976fe08b9ffccb4bbea66f26af6017b9 \
--hash=sha256:159dc4e59a159cb8e4e8f8961eb1fa5d58f93cb1acd1701d8aff38d45e1a84a6 \
--hash=sha256:20b7a68444f536365af42a75ccecb7ab41a896a04acf58432db9e206f4e525d6 \
--hash=sha256:23cff1b267038501b179ccbbd74a821ac4a7192a1852d1d558e562b507d46013 \
--hash=sha256:2c72608e70f053643437bd2be0608f7f1c46d4022e4104d76826f0839199347a \
--hash=sha256:3399dd8a7495bbb2bacd59b84840eef9057826c664472e86c91d675d007137f5 \
--hash=sha256:34422d5a69a60b7e9a07a690094e824b66f5ddc662a5fc600d65b7c174a05f04 \
--hash=sha256:370c68dc5570b394cbaadff50e64d705f64debed30573e5c313c360689b6aadc \
--hash=sha256:3a1018e97aeb24e4f939afcd88211ace472ba566efc5bdf53fd8fd7f41fa7170 \
--hash=sha256:3d5ac5234fb5053850d79dd8eb1015cb0d7d9ed951fa37aa9e6249a19aa4f336 \
--hash=sha256:4313ab9bf6a81206c8ac28fdfcddc0435299dc88cad12cc6305fd0e78b81f9e4 \
--hash=sha256:445ca8d3c5a01309633a0c9db57150312a181146315693273e35d936472df912 \
--hash=sha256:479595a4fbe9ed8f8f72c59717e8cf222da2e4c07b6ae5b65411e6302af9708e \
--hash=sha256:4918fd5f8b43aa7ec031e0fef1ee02deb80b6afd49c85f0790be1dc4ce34cb50 \
--hash=sha256:4aba818dcc7263852aabb172ec27b71d2abca02a593b95fa79351b2774eb1d2b \
--hash=sha256:4e819a806420bc010489f4e741b3036071aba209f2e0989d4750b08b12a9343f \
--hash=sha256:4facc913e10bdba42ec0aee76d029aedda628161a7ce4116b16680a0413f658a \
--hash=sha256:549c3584993772e25f02d0656ac48abdda73169fe347263948cf2b1cead622f3 \
--hash=sha256:5c02fcd2bf45162280613d2e4a1ca3ac558ff921ae4e308ecb307650d3a6ee51 \
--hash=sha256:5f580c651a72b75c39e311343fe6875d6f58cf51c471a97f15a938d9fe4e0d37 \
--hash=sha256:62120ed0de69b3649cc68e2965376048793f466c5a6c4370fb27c16c1beac22d \
--hash=sha256:6295004b2dd37b0835ea5c14a33e00e8cfa3c4add4d587b77287825f3418d310 \
--hash=sha256:65436dce9fdc0aeeb0a0effe0839cb3d6a05f45aa45a4d9f9c60989beca78b9c \
--hash=sha256:684008ec44ad275832a5a152f6e764bbe1914bea10968017b6feaecdad5736e0 \
--hash=sha256:684e52023aec43bdf0250e843e1fdd6febbe831bd9d52da72333fa201aaa2335 \
--hash=sha256:6cc38067209354e16c5609b66285af17a2863a47585bcf75285cab33d4c3b8df \
--hash=sha256:6f2f017c5be19984fbbf55f8af6caba25e62c71293213f044da3ada7091a4455 \
--hash=sha256:743deffdf3b3481da32e8a96887e2aa945ec6685af1cfe2bcc292638c9ba2f48 \
--hash=sha256:7571f19f4a3fd00af9341c7801d1ad1967fc9c3f5e62402683047e7166b9f2b4 \
--hash=sha256:7731728b6568fc286d86745f27f07266de49603a6fdc4d19c87e8c247be452af \
--hash=sha256:785c071c982dce54d44ea0b79cd6dfafddeccdd98cfa5f7b86ef69b381b457d9 \
--hash=sha256:78fddb22b9ef810b63ef341c9fcf6455232d97cfe03938cbc29e2672c436670e \
--hash=sha256:7bb966fdd9217e53abf824f437a5a2d643a38d4fd5fd0ca711b9da683d452969 \
--hash=sha256:7cbc5d9e8a1781e7be17da67b92580d6ce4dcef5819c1b1b89f49d9678cc278c \
--hash=sha256:803b8905b52de78b173d3c1e83df0efb929621e7b7c5766c0843704d5332682f \
--hash=sha256:80b696e8972b81edf0af2a259e1b2a4a661f818fae22e5fa4fa1a995fb4a40fd \
--hash=sha256:81500ed5af2090b4a9157a59dbc89873a25c33db1bb9a8cf123837dcc9765047 \
--hash=sha256:89ec7f2c08937421bbbb8b48c54096fa4f88347946d4747021ad85f1b3021b3c \
--hash=sha256:8ba6745440b9a27336443b0c285d705ce73adb9ec90e2f2004c64d95ab5a7598 \
--hash=sha256:8c91e1763696c0eb66340c4df98623c2d4e77d0746b8f8f2bee2c6883fd1fe18 \
--hash=sha256:8d015604ee6204e76569d2f44e5a210728fa917115bef0d102f4107e622b08d5 \
--hash=sha256:8d1f86f3f4e2388aa3310b50694ac44daefbd1681def26b4519bd050a398dc5a \
--hash=sha256:8f83b6fd3dc3ba94d2b22717f9c8b8512354fd95221ac661784df2769ea9bba9 \
--hash=sha256:8fc6976a3395fe4d1fbeb984adaa8ec652a1e12f36b56ec8c236e5117b585427 \
--hash=sha256:904c883cf10a975b02ab3478bce652f0f5346a2c28d0a8521d97bb23c323cc8b \
--hash=sha256:911742856ce98d879acbea33fcc03c1d8dc1106234c5e7d068932c945db209c0 \
--hash=sha256:91797b98f5e34b6a49f54be33f72e2fb658018ae532be2f79f7c63b4ae225145 \
--hash=sha256:95399831a206211d6bc40224af1c635cb8790ddd5c7493e0bd03b85711076a53 \
--hash=sha256:956b58d692f235cfbf5b4f3abd6d99bf102f161ccfe20d2fd0904f51c72c4c66 \
--hash=sha256:98c1165f3809ce7774f05cb74e5408cd3aa93ee8573ae959a97a53db3ca3180d \
--hash=sha256:9ab40412f8cd6f615bfedea40c8bf0407d41bf83b96f6fc9ff34976d6b7037fd \
--hash=sha256:9df1bfef97db938469ef0a7354b2d591a2d438bc497b2c489471bec0e6baf7c4 \
--hash=sha256:a01fe2305e6232ef3e8f40bfc0f0f3a04def9aab514910fa4203bafbc0bb4682 \
--hash=sha256:a70b51f55fd954d1f194271695821dd62054d949efd6368d8be64edd37f55c86 \
--hash=sha256:a7ccdd1c4a3472a7533b0a7aa9ee34c9a2bef859ba86deec07aff2ad7e0c3b94 \
--hash=sha256:b340cccad138ecb363324aa26893963dcabb02bb25e440ebdf42e30963f1a4e0 \
--hash=sha256:b74586dd0b039c62416034f811d7ee62810174bb70dffcca6439f5236249eb09 \
--hash=sha256:b9d320b3bf82a39f248769fc7f188e00f93526cc0fe739cfa197868633d44701 \
--hash=sha256:ba2336d6548dee3117520545cfe44dc28a250aa091f8281d28804aa8d707d93d \
--hash=sha256:ba8122e3bb94ecda29a8de4cf889f600171424ea586847aa92c334772d200331 \
--hash=sha256:bd727ad276bb91928879f3aa6396c9a1d34e5e180dce40578421a691eeb77f47 \
--hash=sha256:c21fc21a4c7480479d12fd8e679b699f744f76bb05f53a1d14182b31f55aac76 \
--hash=sha256:c2d0e7cbb6341e830adcbfa2479fdeebbfbb328f11edd6b5675674e7a1e37730 \
--hash=sha256:c2ef6f7990b6e8758fe48ad08f7e2f66c8f11dc66e24093304b87cae9037bb4a \
--hash=sha256:c4ed75ea6892a56896d78f11006161eea52c45a14994794bcfa1654430984b22 \
--hash=sha256:cccc79a9be9b64c881f18305a7c715ba199e471a3973faeb7ba84172abb3f317 \
--hash=sha256:d0800631e565c47520aaa04ae38b96abc5196fe8b4aa9bd864445bd2b5848a7a \
--hash=sha256:d2da13568eff02b30fd54fccd1e042a70fe920d816616fda4bf54ec705668d81 \
--hash=sha256:d61ae114d2a2311f61d90c2ef1358518e8f05eafda76eaf9c772a077e0b465ec \
--hash=sha256:d83c2bc678453646f1a18f8db1e927a2d3f4935031b9ad8a76e56760461105dd \
--hash=sha256:dd5acc0a7d38fdc7a3a6fd3ad14c880819008ecb3379626e56b163165162cc46 \
--hash=sha256:df79012ebf6f4efb8d307b1328226aef24ca446b3ff8d0e30202d7ebcb977a8c \
--hash=sha256:e0a2df336d1135a0b3a67f3bbf78a75f69562c1199ed9935372b82215cddd6e2 \
--hash=sha256:e2f142b45c6fed48166faeb4303b4b58c9fcd827da63f4cf0a123c3480ae11fb \
--hash=sha256:e697e1c0238133589e00c244a8b676bc2cfc3ab4961318d902040d099fec7483 \
--hash=sha256:e757d475953269fbf4b441207bb7dbdd1c43180711b6208e129b637792ac0b93 \
--hash=sha256:e87ab229332ceb127a165612d839ab87795972102cb9830e5f12b8c9a5c1b508 \
--hash=sha256:ea355eb43b11764cf799dda62c658c4d2fdb16af41f59bb1ccfec517b60bcb07 \
--hash=sha256:ec7e0043b91115f427998febaa2beb82c82df708168b35ece3accb610b91fac1 \
--hash=sha256:eeaa0b5328b785abc344acc6241cffde50dc394a0644a968add75fcefe15b9d4 \
--hash=sha256:f2d80a6749724b37853ece57988b39c4e79d2b5fe2869a86e8aeae3bbeef9eb0 \
--hash=sha256:fa454d26f2e87ad661c4f0c5a5fe4cf6aab1e307d1b94f16ffdfcb089ba685c0 \
--hash=sha256:fb83cc090eac63c006871fd24db5e30a1f282faa46328572661c0a24a2323a08 \
--hash=sha256:fd80d1280d473500d8086d104962a82d77bfbf2b118053824b7be28cd5a79ea5
# via mkdocs-material
requests==2.31.0 \
--hash=sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f \

View file

@ -442,10 +442,72 @@ BunkerNet is a crowdsourced database of malicious requests shared between all Bu
If you enable BunkerNet, malicious requests will be sent to a remote server and will be analyzed by our systems. By doing so, we can extract malicious data from everyone's reports and give back the results to each BunkerWeb instances participating into BunkerNet.
At the moment, that feature should be considered in "beta". We only extract malicious IP and we are very strict about how we do it to avoid any "poisoning". We strongly recommend activating it (which is the default) because the more instances participate, the more data we have to improve the algorithm.
Besides the enhanced security, enabling BunkerNet will let you benefit from extra features such as the integration with CrowdSec Console.
The setting used to enable or disable BunkerNet is `USE_BUNKERNET` (default : `yes`).
### CrowdSec Console integration
If you don't already know about it, [CrowdSec](https://www.crowdsec.net/?utm_campaign=bunkerweb&utm_source=doc) is an open-source cybersecurity solution leveraging crowdsourced intelligence to mitigate cyber threats. Think of it like Waze but applied to cybersecurity : when a specific server is attacked, other systems around the globe will be informed and protected from the same attackers. You will find more information on their website [here](https://www.crowdsec.net/about?utm_campaign=bunkerweb&utm_source=blog).
Thanks to a partnership with CrowdSec, you can enroll your BunkerWeb instances to your [CrowdSec Console](https://app.crowdsec.net/signup?utm_source=external-blog&utm_medium=cta&utm_campaign=bunker-web-integration). In other words, the attacks blocked by BunkerWeb will be visible the same way it does for attacks blocked by CrowdSec Security Engines.
Please note that CrowdSec doesn't need to be installed at all (even if we recommend you to try it with the [CrowdSec plugin for BunkerWeb](https://github.com/bunkerity/bunkerweb-plugins/tree/main/crowdsec) to enhance the security of your web services) and you can still enroll your Security Engines into the same Console account.
**Step #1 : create your CrowdSec Console account**
Go to the [CrowdSec Console](https://app.crowdsec.net/signup?utm_source=external-blog&utm_medium=cta&utm_campaign=bunker-web-integration) and register your account if you don't already have one. Once it's done, write down your enroll key by going to "Security Engines", then "Engines" and click on "Add Security Engine" :
<figure markdown>
![Overview](assets/img/crowdity1.png){ align=center }
<figcaption>Get your Crowdsec Console enroll key</figcaption>
</figure>
**Step #2 : get your BunkerNet ID**
Activating the BunkerNet feature (which is the case by default) is mandatory if you want to enroll your BunkerWeb instance(s) into your CrowdSec console. You can do it by setting `USE_BUNKERNET` to `yes`.
Get your BunkerNet ID on Docker :
```shell
docker exec my-bw-scheduler cat /var/cache/bunkerweb/bunkernet/instance.id
```
Get your BunkerNet ID on Linux :
```shell
cat /var/cache/bunkerweb/bunkernet/instance.id
```
**Step #3 : enroll your instance using the Panel**
Once you have noted your BunkerNet ID and CrowdSec Console enroll key, you can [order the free product "BunkerNet / CrowdSec" on the Panel](https://panel.bunkerweb.io/order/bunkernet/11?utm_campaign=self&utm_source=doc). Please note that you will need to create an account if you don't already have one.
You can now select the "BunkerNet / CrowdSec" service and fill out the form by pasting your BunkerNet ID and CrowdSec Console enroll key :
<figure markdown>
![Overview](assets/img/crowdity2.png){ align=center }
<figcaption>Enroll your BunkerWeb instance into the CrowdSec Console</figcaption>
</figure>
**Step #4 : accept new security engine on the Console**
Last but not least, you need to go back to your CrowdSec Console and accept the new Security Engine :
<figure markdown>
![Overview](assets/img/crowdity3.png){ align=center }
<figcaption>Accept enroll into the CrowdSec Console</figcaption>
</figure>
**Congratulations, your BunkerWeb instance is now enrolled into your CrowdSec Console !**
Pro tip : when viewing your alerts, click on "columns" and tick the "context" checkbox to get access to BunkerWeb specific data.
<figure markdown>
![Overview](assets/img/crowdity4.png){ align=center }
<figcaption>BunkerWeb data shown in the context column</figcaption>
</figure>
## DNSBL
STREAM support :white_check_mark:
@ -640,9 +702,11 @@ The Reporting plugin provides a comprehensive solution for regular reporting of
## Backup and restore
### Backup
STREAM support :white_check_mark:
### Automated backup
#### Automated backup
!!! warning "Information for Red Hat Enterprise Linux (RHEL) 8.9 users"
If you are using **RHEL 8.9** and plan on using an **external database**, you will need to install the `mysql-community-client` package to ensure the `mysqldump` command is available. You can install the package by executing the following commands:
@ -696,7 +760,7 @@ Data is invaluable, especially in digital environments where it's susceptible to
| `BACKUP_SCHEDULE` | `daily` | global | no | The frequency of the backup |
| `BACKUP_ROTATION` | `7` | global | no | The number of backups to keep |
### Manual backup
#### Manual backup
To manually initiate a backup, execute the following command:
@ -725,7 +789,11 @@ You can also specify a custom directory for the backup by providing the `BACKUP_
=== "Docker"
```bash
docker exec -it -e BACKUP_DIRECTORY=/path/to/backup/directory -v /path/to/backup/directory:/path/to/backup/directory <scheduler_container> bwcli plugin backup save
docker exec -it -e BACKUP_DIRECTORY=/path/to/backup/directory <scheduler_container> bwcli plugin backup save
```
```bash
docker cp <scheduler_container>:/path/to/backup/directory /path/to/backup/directory
```
!!! note "Specifications for MariaDB/MySQL"
@ -762,7 +830,7 @@ You can also specify a custom directory for the backup by providing the `BACKUP_
...
```
### Manual restore
#### Manual restore
To manually initiate a restore, execute the following command:
@ -791,7 +859,11 @@ You can also specify a custom backup file for the restore by providing the path
=== "Docker"
```bash
docker exec -it -v /path/to/backup/file:/path/to/backup/file <scheduler_container> bwcli plugin backup restore /path/to/backup/file
docker cp /path/to/backup/file <scheduler_container>:/path/to/backup/file
```
```bash
docker exec -it <scheduler_container> bwcli plugin backup restore /path/to/backup/file
```
!!! example "In case of failure"
@ -807,5 +879,291 @@ You can also specify a custom backup file for the restore by providing the path
=== "Docker"
```bash
docker exec -it -e BACKUP_DIRECTORY=/var/tmp/bunkerweb/backups -v /var/tmp/bunkerweb/backups:/var/tmp/bunkerweb/backups <scheduler_container> bwcli plugin backup restore
docker cp <scheduler_container>:/var/tmp/bunkerweb/backups /var/tmp/bunkerweb/backups
```
```bash
docker exec -it -e BACKUP_DIRECTORY=/var/tmp/bunkerweb/backups <scheduler_container> bwcli plugin backup restore
```
### Backup S3 <img src='../assets/img/pro-icon.svg' alt='crow pro icon' height='24px' width='24px' style="transform : translateY(3px);"> (PRO)
STREAM support :white_check_mark:
The Backup S3 tool seamlessly automates data protection, similar to the community backup plugin. However, it stands out by securely storing backups directly in an S3 bucket.
By activating this feature, you're proactively safeguarding your **data's integrity**. Storing backups **remotely** shields crucial information from threats like **hardware failures**, **cyberattacks**, or **natural disasters**. This ensures both **security** and **availability**, enabling swift recovery during **unexpected events**, preserving **operational continuity**, and ensuring **peace of mind**.
!!! warning "Information for Red Hat Enterprise Linux (RHEL) 8.9 users"
If you are using **RHEL 8.9** and plan on using an **external database**, you will need to install the `mysql-community-client` package to ensure the `mysqldump` command is available. You can install the package by executing the following commands:
=== "MySQL/MariaDB"
1. **Install the MySQL repository configuration package**
```bash
sudo dnf install https://dev.mysql.com/get/mysql80-community-release-el8-9.noarch.rpm
```
2. **Enable the MySQL repository**
```bash
sudo dnf config-manager --enable mysql80-community
```
3. **Install the MySQL client**
```bash
sudo dnf install mysql-community-client
```
=== "PostgreSQL"
1. **Install the PostgreSQL repository configuration package**
```bash
dnf install "https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-$(uname -m)/pgdg-redhat-repo-latest.noarch.rpm"
```
2. **Install the PostgreSQL client**
```bash
dnf install postgresql<version>
```
**List of features**
- Automatic data backup to an S3 bucket
- Flexible scheduling options: daily, weekly, or monthly
- Rotation management for controlling the number of backups to keep
- Customizable compression level for backup files
**List of settings**
| Setting | Default | Context | Description |
| ----------------------------- | ------- | ------- | ------------------------------------------------ |
| `USE_BACKUP_S3` | `no` | global | Enable or disable the S3 backup feature |
| `BACKUP_S3_SCHEDULE` | `daily` | global | Frequency of the backup (daily, weekly, monthly) |
| `BACKUP_S3_ROTATION` | `7` | global | Number of backups to keep |
| `BACKUP_S3_ENDPOINT` | | global | S3 endpoint |
| `BACKUP_S3_BUCKET` | | global | S3 bucket name |
| `BACKUP_S3_REGION` | | global | S3 region |
| `BACKUP_S3_ACCESS_KEY_ID` | | global | S3 access key ID |
| `BACKUP_S3_ACCESS_KEY_SECRET` | | global | S3 access key secret |
| `BACKUP_S3_COMP_LEVEL` | `6` | global | Compression level of the backup zip file (1-9) |
#### Manual backup
To manually initiate a backup, execute the following command:
=== "Linux"
```bash
bwcli plugin backup_s3 save
```
=== "Docker"
```bash
docker exec -it <scheduler_container> bwcli plugin backup_s3 save
```
This command will create a backup of your database and store it in the S3 bucket specified in the `BACKUP_S3_BUCKET` setting.
You can also specify a custom S3 bucket for the backup by providing the `BACKUP_S3_BUCKET` environment variable when executing the command:
=== "Linux"
```bash
BACKUP_S3_BUCKET=your-bucket-name bwcli plugin backup_s3 save
```
=== "Docker"
```bash
docker exec -it -e BACKUP_S3_BUCKET=your-bucket-name <scheduler_container> bwcli plugin backup_s3 save
```
!!! note "Specifications for MariaDB/MySQL"
In case you are using MariaDB/MySQL, you may encounter the following error when trying to backup your database:
```bash
caching_sha2_password could not be loaded: Error loading shared library /usr/lib/mariadb/plugin/caching_sha2_password.so
```
To resolve this issue, you can execute the following command to change the authentication plugin to `mysql_native_password`:
```sql
ALTER USER 'yourusername'@'localhost' IDENTIFIED WITH mysql_native_password BY 'youpassword';
```
If you're using the Docker integration, you can add the following command to the `docker-compose.yml` file to automatically change the authentication plugin:
=== "MariaDB"
```yaml
bw-db:
image: mariadb:<version>
command: --default-authentication-plugin=mysql_native_password
...
```
=== "MySQL"
```yaml
bw-db:
image: mysql:<version>
command: --default-authentication-plugin=mysql_native_password
...
```
#### Manual restore
To manually initiate a restore, execute the following command:
=== "Linux"
```bash
bwcli plugin backup_s3 restore
```
=== "Docker"
```bash
docker exec -it <scheduler_container> bwcli plugin backup_s3 restore
```
This command will create a temporary backup of your database in the S3 bucket specified in the `BACKUP_S3_BUCKET` setting and restore your database to the latest backup available in the bucket.
You can also specify a custom backup file for the restore by providing the path to it as an argument when executing the command:
=== "Linux"
```bash
bwcli plugin backup_s3 restore s3_backup_file.zip
```
=== "Docker"
```bash
docker exec -it <scheduler_container> bwcli plugin backup restore s3_backup_file.zip
```
!!! example "In case of failure"
Don't worry if the restore fails, you can always restore your database to the previous state by executing the command again as a backup is created before the restore:
=== "Linux"
```bash
bwcli plugin backup_s3 restore
```
=== "Docker"
```bash
docker exec -it <scheduler_container> bwcli plugin backup_s3 restore
```
## Migration of BunkerWeb
### Migration <img src='../assets/img/pro-icon.svg' alt='crow pro icon' height='24px' width='24px' style="transform : translateY(3px);"> (PRO)
STREAM support :white_check_mark:
The Migration tool **revolutionizes** BunkerWeb configuration transfers between instances with its **user-friendly web interface**, simplifying the entire migration journey. Whether you're upgrading systems, scaling infrastructure, or transitioning environments, this tool empowers you to effortlessly transfer **settings, preferences, and data** with unmatched ease and confidence. Say goodbye to cumbersome manual processes and hello to a **seamless, hassle-free migration experience**.
**List of features**
- **Effortless Migration:** Easily transfer BunkerWeb configurations between instances without the complexities of manual procedures.
- **Intuitive Web Interface:** Navigate through the migration process effortlessly with a user-friendly web interface designed for intuitive operation.
- **Cross-Database Compatibility:** Enjoy seamless migration across various database platforms, including SQLite, MySQL, MariaDB, and PostgreSQL, ensuring compatibility with your preferred database environment.
#### Create a migration file
To manually create a migration file, execute the following command:
=== "Linux"
```bash
bwcli plugin migration create /path/to/migration/file
```
=== "Docker"
1. Create a migration file:
```bash
docker exec -it <scheduler_container> bwcli plugin migration create /path/to/migration/file
```
2. Copy the migration file to your local machine:
```bash
docker cp <scheduler_container>:/path/to/migration/file /path/to/migration/file
```
This command will create a backup of your database and store it in the backup directory specified in the command.
!!! note "Specifications for MariaDB/MySQL"
In case you are using MariaDB/MySQL, you may encounter the following error when trying to backup your database:
```bash
caching_sha2_password could not be loaded: Error loading shared library /usr/lib/mariadb/plugin/caching_sha2_password.so
```
To resolve this issue, you can execute the following command to change the authentication plugin to `mysql_native_password`:
```sql
ALTER USER 'yourusername'@'localhost' IDENTIFIED WITH mysql_native_password BY 'youpassword';
```
If you're using the Docker integration, you can add the following command to the `docker-compose.yml` file to automatically change the authentication plugin:
=== "MariaDB"
```yaml
bw-db:
image: mariadb:<version>
command: --default-authentication-plugin=mysql_native_password
...
```
=== "MySQL"
```yaml
bw-db:
image: mysql:<version>
command: --default-authentication-plugin=mysql_native_password
...
```
#### Initialize a migration
To manually initialize a migration, execute the following command:
=== "Linux"
```bash
bwcli plugin migration migrate /path/to/migration/file
```
=== "Docker"
1. Copy the migration file to the container:
```bash
docker cp /path/to/migration/file <scheduler_container>:/path/to/migration/file
```
2. Initialize the migration:
```bash
docker exec -it <scheduler_container> bwcli plugin migration migrate /path/to/migration/file
```
This command seamlessly migrates your BunkerWeb data to precisely match the configuration outlined in the migration file.

View file

@ -15,41 +15,42 @@ When settings are considered as "multiple", it means that you can have multiple
STREAM support :warning:
| Setting | Default | Context |Multiple| Description |
|------------------------------|------------------------------------------------------------------------------------------------------------------------|---------|--------|------------------------------------------------------------|
|`IS_LOADING` |`no` |global |no |Internal use : set to yes when BW is loading. |
|`NGINX_PREFIX` |`/etc/nginx/` |global |no |Where nginx will search for configurations. |
|`HTTP_PORT` |`8080` |global |no |HTTP port number which bunkerweb binds to. |
|`HTTPS_PORT` |`8443` |global |no |HTTPS port number which bunkerweb binds to. |
|`MULTISITE` |`no` |global |no |Multi site activation. |
|`SERVER_NAME` |`www.example.com` |multisite|no |List of the virtual hosts served by bunkerweb. |
|`WORKER_PROCESSES` |`auto` |global |no |Number of worker processes. |
|`WORKER_RLIMIT_NOFILE` |`2048` |global |no |Maximum number of open files for worker processes. |
|`WORKER_CONNECTIONS` |`1024` |global |no |Maximum number of connections per worker. |
|`LOG_FORMAT` |`$host $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"`|global |no |The format to use for access logs. |
|`LOG_LEVEL` |`notice` |global |no |The level to use for error logs. |
|`DNS_RESOLVERS` |`127.0.0.11` |global |no |DNS addresses of resolvers to use. |
|`DATASTORE_MEMORY_SIZE` |`64m` |global |no |Size of the internal datastore. |
|`CACHESTORE_MEMORY_SIZE` |`64m` |global |no |Size of the internal cachestore. |
|`CACHESTORE_IPC_MEMORY_SIZE` |`16m` |global |no |Size of the internal cachestore (ipc). |
|`CACHESTORE_MISS_MEMORY_SIZE` |`16m` |global |no |Size of the internal cachestore (miss). |
|`CACHESTORE_LOCKS_MEMORY_SIZE`|`16m` |global |no |Size of the internal cachestore (locks). |
|`USE_API` |`yes` |global |no |Activate the API to control BunkerWeb. |
|`API_HTTP_PORT` |`5000` |global |no |Listen port number for the API. |
|`API_LISTEN_IP` |`0.0.0.0` |global |no |Listen IP address for the API. |
|`API_SERVER_NAME` |`bwapi` |global |no |Server name (virtual host) for the API. |
|`API_WHITELIST_IP` |`127.0.0.0/8` |global |no |List of IP/network allowed to contact the API. |
|`AUTOCONF_MODE` |`no` |global |no |Enable Autoconf Docker integration. |
|`SWARM_MODE` |`no` |global |no |Enable Docker Swarm integration. |
|`KUBERNETES_MODE` |`no` |global |no |Enable Kubernetes integration. |
|`SERVER_TYPE` |`http` |multisite|no |Server type : http or stream. |
|`LISTEN_STREAM` |`yes` |multisite|no |Enable listening for non-ssl (passthrough). |
|`LISTEN_STREAM_PORT` |`1337` |multisite|no |Listening port for non-ssl (passthrough). |
|`LISTEN_STREAM_PORT_SSL` |`4242` |multisite|no |Listening port for ssl (passthrough). |
|`USE_UDP` |`no` |multisite|no |UDP listen instead of TCP (stream). |
|`USE_IPV6` |`no` |global |no |Enable IPv6 connectivity. |
|`IS_DRAFT` |`no` |multisite|no |Internal use : set to yes when the service is in draft mode.|
|`TIMERS_LOG_LEVEL` |`debug` |global |no |Log level for timers. |
| Setting | Default | Context |Multiple| Description |
|------------------------------|------------------------------------------------------------------------------------------------------------------------|---------|--------|--------------------------------------------------------------------------------------------|
|`IS_LOADING` |`no` |global |no |Internal use : set to yes when BW is loading. |
|`NGINX_PREFIX` |`/etc/nginx/` |global |no |Where nginx will search for configurations. |
|`HTTP_PORT` |`8080` |global |no |HTTP port number which bunkerweb binds to. |
|`HTTPS_PORT` |`8443` |global |no |HTTPS port number which bunkerweb binds to. |
|`MULTISITE` |`no` |global |no |Multi site activation. |
|`SERVER_NAME` |`www.example.com` |multisite|no |List of the virtual hosts served by bunkerweb. |
|`WORKER_PROCESSES` |`auto` |global |no |Number of worker processes. |
|`WORKER_RLIMIT_NOFILE` |`2048` |global |no |Maximum number of open files for worker processes. |
|`WORKER_CONNECTIONS` |`1024` |global |no |Maximum number of connections per worker. |
|`LOG_FORMAT` |`$host $remote_addr - $remote_user [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent"`|global |no |The format to use for access logs. |
|`LOG_LEVEL` |`notice` |global |no |The level to use for error logs. |
|`DNS_RESOLVERS` |`127.0.0.11` |global |no |DNS addresses of resolvers to use. |
|`DATASTORE_MEMORY_SIZE` |`64m` |global |no |Size of the internal datastore. |
|`CACHESTORE_MEMORY_SIZE` |`64m` |global |no |Size of the internal cachestore. |
|`CACHESTORE_IPC_MEMORY_SIZE` |`16m` |global |no |Size of the internal cachestore (ipc). |
|`CACHESTORE_MISS_MEMORY_SIZE` |`16m` |global |no |Size of the internal cachestore (miss). |
|`CACHESTORE_LOCKS_MEMORY_SIZE`|`16m` |global |no |Size of the internal cachestore (locks). |
|`USE_API` |`yes` |global |no |Activate the API to control BunkerWeb. |
|`API_HTTP_PORT` |`5000` |global |no |Listen port number for the API. |
|`API_LISTEN_IP` |`0.0.0.0` |global |no |Listen IP address for the API. |
|`API_SERVER_NAME` |`bwapi` |global |no |Server name (virtual host) for the API. |
|`API_WHITELIST_IP` |`127.0.0.0/8` |global |no |List of IP/network allowed to contact the API. |
|`AUTOCONF_MODE` |`no` |global |no |Enable Autoconf Docker integration. |
|`SWARM_MODE` |`no` |global |no |Enable Docker Swarm integration. |
|`KUBERNETES_MODE` |`no` |global |no |Enable Kubernetes integration. |
|`SERVER_TYPE` |`http` |multisite|no |Server type : http or stream. |
|`LISTEN_STREAM` |`yes` |multisite|no |Enable listening for non-ssl (passthrough). |
|`LISTEN_STREAM_PORT` |`1337` |multisite|no |Listening port for non-ssl (passthrough). |
|`LISTEN_STREAM_PORT_SSL` |`4242` |multisite|no |Listening port for ssl (passthrough). |
|`USE_UDP` |`no` |multisite|no |UDP listen instead of TCP (stream). |
|`USE_IPV6` |`no` |global |no |Enable IPv6 connectivity. |
|`IS_DRAFT` |`no` |multisite|no |Internal use : set to yes when the service is in draft mode. |
|`TIMERS_LOG_LEVEL` |`debug` |global |no |Log level for timers. |
|`OVERRIDE_INSTANCES` | |global |no |List of BunkerWeb instances separated with spaces (format : fqdn-or-ip:5000 fqdn-or-ip:5000)|
## Antibot

View file

@ -470,3 +470,10 @@ In case you lost your UI credentials or have 2FA issues, you can connect to the
```
You should now be able to log into the web UI only using your username and password.
**Upload plugin**
It may not be possible to upload a plugin from the UI in certain situations:
- Missing package to manage compressed files on your integration, in which case you will need to add the necessary packages
- Safari browser : the 'safe mode' may prevent you from being able to add a plugin. You will need to make the necessary changes on your machine

View file

@ -17,7 +17,11 @@
=== "Docker"
```bash
docker exec -it -e BACKUP_DIRECTORY=/path/to/backup/directory -v /path/to/backup/directory:/path/to/backup/directory <scheduler_container> bwcli plugin backup save
docker exec -it -e BACKUP_DIRECTORY=/path/to/backup/directory <scheduler_container> bwcli plugin backup save
```
```bash
docker cp <scheduler_container>:/path/to/backup/directory /path/to/backup/directory
```
=== "Linux"
@ -47,13 +51,13 @@
=== "PostgreSQL"
1. **Install the PostgreSQL repository configuration package**
4. **Install the PostgreSQL repository configuration package**
```bash
dnf install "https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-$(uname -m)/pgdg-redhat-repo-latest.noarch.rpm"
```
2. **Install the PostgreSQL client**
5. **Install the PostgreSQL client**
```bash
dnf install postgresql<version>
@ -145,13 +149,13 @@
=== "Linux"
1. **Stop the services**:
3. **Stop the services**:
```bash
systemctl stop bunkerweb-scheduler
systemctl stop bunkerweb-ui
```
2. **Update BunkerWeb**:
4. **Update BunkerWeb**:
- Update BunkerWeb to the latest version by following the instructions in the [integration Linux page](integrations.md#linux).

View file

@ -855,6 +855,7 @@ After a successful login/password combination, you will be prompted to enter you
- www.example.com_REVERSE_PROXY_URL=/changeme
- www.example.com_REVERSE_PROXY_HOST=http://bw-ui:7000
- www.example.com_INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504
- www.example.com_MAX_CLIENT_SIZE=50m
networks:
- bw-universe
- bw-services
@ -1040,7 +1041,7 @@ After a successful login/password combination, you will be prompted to enter you
- "bunkerweb.REVERSE_PROXY_URL=/changeme"
- "bunkerweb.REVERSE_PROXY_HOST=http://bw-ui:7000"
- "bunkerweb.INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504"
- "bunkerweb.MAX_CLIENT_SIZE=50m"
volumes:
bw-data:
@ -1195,7 +1196,7 @@ After a successful login/password combination, you will be prompted to enter you
- "bunkerweb.REVERSE_PROXY_HOST=http://bw-ui:7000"
- "bunkerweb.REVERSE_PROXY_INTERCEPT_ERRORS=no"
- "bunkerweb.INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504"
- "bunkerweb.MAX_CLIENT_SIZE=50m"
volumes:
bw-data:
@ -1549,6 +1550,7 @@ After a successful login/password combination, you will be prompted to enter you
annotations:
bunkerweb.io/www.example.com_USE_UI: "yes"
bunkerweb.io/www.example.com_INTERCEPTED_ERROR_CODES: '400 404 405 413 429 500 501 502 503 504'
bunkerweb.io/www.example.com_MAX_CLIENT_SIZE: '50m'
spec:
rules:
- host: www.example.com
@ -1602,6 +1604,7 @@ After a successful login/password combination, you will be prompted to enter you
www.example.com_REVERSE_PROXY_URL=/changeme
www.example.com_REVERSE_PROXY_HOST=http://127.0.0.1:7000
www.example.com_INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504
www.example.com_MAX_CLIENT_SIZE=50m
```
Don't forget to restart the `bunkerweb` service :
@ -1635,6 +1638,7 @@ After a successful login/password combination, you will be prompted to enter you
www.example.com_REVERSE_PROXY_URL=/changeme
www.example.com_REVERSE_PROXY_HOST=http://127.0.0.1:7000
www.example.com_INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504
www.example.com_MAX_CLIENT_SIZE=50m
```
The variable `enable_ui` can be set to `true` in order to activate the web UI service and the variable `custom_ui` can be used to specify the configuration file for the web UI :
@ -1709,6 +1713,7 @@ After a successful login/password combination, you will be prompted to enter you
www.example.com_REVERSE_PROXY_URL=/changeme
www.example.com_REVERSE_PROXY_HOST=http://127.0.0.1:7000
www.example.com_INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504
www.example.com_MAX_CLIENT_SIZE=50m
```
Don't forget to restart the `bunkerweb` service :

View file

@ -22,6 +22,7 @@ services:
- www.example.com_REVERSE_PROXY_URL=/changeme
- www.example.com_REVERSE_PROXY_HOST=http://bw-ui:7000
- www.example.com_INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504
- www.example.com_MAX_CLIENT_SIZE=50m
networks:
- bw-universe
- bw-services

View file

@ -15,5 +15,5 @@ USE_GZIP=yes
www.example.com_USE_UI=yes
www.example.com_USE_REVERSE_PROXY=yes
www.example.com_REVERSE_PROXY_URL=/admin
www.example.com_REVERSE_PROXY_HOST=http://bw-ui:7000
www.example.com_REVERSE_PROXY_HOST=http://127.0.0.1:7000
www.example.com_INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504

View file

@ -16,6 +16,6 @@ USE_GZIP=yes
www.example.com_USE_UI=yes
www.example.com_USE_REVERSE_PROXY=yes
www.example.com_REVERSE_PROXY_URL=/admin
www.example.com_REVERSE_PROXY_HOST=http://bw-ui:7000
www.example.com_REVERSE_PROXY_HOST=http://127.0.0.1:7000
www.example.com_INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504
EXTERNAL_PLUGIN_URLS=https://github.com/bunkerity/bunkerweb-plugins/archive/refs/heads/dev.zip

View file

@ -86,6 +86,7 @@ services:
- "bunkerweb.REVERSE_PROXY_URL=/changeme"
- "bunkerweb.REVERSE_PROXY_HOST=http://bw-ui:7000"
- "bunkerweb.INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504"
- "bunkerweb.MAX_CLIENT_SIZE=50m"
volumes:
bw-data:

View file

@ -86,6 +86,7 @@ services:
- "bunkerweb.REVERSE_PROXY_URL=/changeme"
- "bunkerweb.REVERSE_PROXY_HOST=http://bw-ui:7000"
- "bunkerweb.INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504"
- "bunkerweb.MAX_CLIENT_SIZE=50m"
volumes:
bw-data:

View file

@ -85,6 +85,7 @@ services:
- "bunkerweb.REVERSE_PROXY_URL=/changeme"
- "bunkerweb.REVERSE_PROXY_HOST=http://bw-ui:7000"
- "bunkerweb.INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504"
- "bunkerweb.MAX_CLIENT_SIZE=50m"
volumes:
bw-data:

View file

@ -76,6 +76,7 @@ services:
- "bunkerweb.REVERSE_PROXY_URL=/changeme"
- "bunkerweb.REVERSE_PROXY_HOST=http://bw-ui:7000"
- "bunkerweb.INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504"
- "bunkerweb.MAX_CLIENT_SIZE=50m"
volumes:
bw-data:

View file

@ -21,6 +21,7 @@ services:
- www.example.com_REVERSE_PROXY_URL=/changeme
- www.example.com_REVERSE_PROXY_HOST=http://bw-ui:7000
- www.example.com_INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504
- www.example.com_MAX_CLIENT_SIZE=50m
networks:
- bw-universe
- bw-services

View file

@ -21,6 +21,7 @@ services:
- www.example.com_REVERSE_PROXY_URL=/changeme
- www.example.com_REVERSE_PROXY_HOST=http://bw-ui:7000
- www.example.com_INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504
- www.example.com_MAX_CLIENT_SIZE=50m
networks:
- bw-universe
- bw-services

View file

@ -22,6 +22,7 @@ services:
- www.example.com_REVERSE_PROXY_URL=/changeme
- www.example.com_REVERSE_PROXY_HOST=http://bw-ui:7000
- www.example.com_INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504
- www.example.com_MAX_CLIENT_SIZE=50m
networks:
- bw-universe
- bw-services

View file

@ -17,6 +17,7 @@ services:
- www.example.com_REVERSE_PROXY_URL=/changeme
- www.example.com_REVERSE_PROXY_HOST=http://bw-ui:7000
- www.example.com_INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504
- www.example.com_MAX_CLIENT_SIZE=50m
networks:
- bw-universe
- bw-services

View file

@ -312,6 +312,7 @@ metadata:
annotations:
bunkerweb.io/www.example.com_USE_UI: "yes"
bunkerweb.io/www.example.com_INTERCEPTED_ERROR_CODES: "400 404 405 413 429 500 501 502 503 504"
bunkerweb.io/www.example.com_MAX_CLIENT_SIZE: "50m"
spec:
rules:
- host: www.example.com

View file

@ -311,6 +311,7 @@ metadata:
annotations:
bunkerweb.io/www.example.com_USE_UI: "yes"
bunkerweb.io/www.example.com_INTERCEPTED_ERROR_CODES: "400 404 405 413 429 500 501 502 503 504"
bunkerweb.io/www.example.com_MAX_CLIENT_SIZE: "50m"
spec:
rules:
- host: www.example.com

View file

@ -311,6 +311,7 @@ metadata:
annotations:
bunkerweb.io/www.example.com_USE_UI: "yes"
bunkerweb.io/www.example.com_INTERCEPTED_ERROR_CODES: "400 404 405 413 429 500 501 502 503 504"
bunkerweb.io/www.example.com_MAX_CLIENT_SIZE: "50m"
spec:
rules:
- host: www.example.com

View file

@ -104,6 +104,7 @@ services:
- "bunkerweb.REVERSE_PROXY_URL=/changeme"
- "bunkerweb.REVERSE_PROXY_HOST=http://bw-ui:7000"
- "bunkerweb.INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504"
- "bunkerweb.MAX_CLIENT_SIZE=50m"
volumes:
bw-data:

View file

@ -104,6 +104,7 @@ services:
- "bunkerweb.REVERSE_PROXY_URL=/changeme"
- "bunkerweb.REVERSE_PROXY_HOST=http://bw-ui:7000"
- "bunkerweb.INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504"
- "bunkerweb.MAX_CLIENT_SIZE=50m"
volumes:
bw-data:

View file

@ -103,6 +103,7 @@ services:
- "bunkerweb.REVERSE_PROXY_URL=/changeme"
- "bunkerweb.REVERSE_PROXY_HOST=http://bw-ui:7000"
- "bunkerweb.INTERCEPTED_ERROR_CODES=400 404 405 413 429 500 501 502 503 504"
- "bunkerweb.MAX_CLIENT_SIZE=50m"
volumes:
bw-data:

View file

@ -10,25 +10,24 @@
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<p class="core-card-text mt-4 mb-2">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#antibot" class="hover:brightness-90 cursor-pointer mt-2 underline text-primary">in the documentation</a>.</p>
<p class="core-card-text-doc">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#antibot" class="core-card-text-doc-link">in the documentation</a>.</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
{% if pre_render["status"] and pre_render["status"] == "ko" or "error" in pre_render["data"] %}
<!-- end info -->
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ko" or "error" in pre_render.get("data", {}) or pre_render.get("data") is not mapping %} <div class="core-layout-separator"></div>
<div class="my-2 flex justify-center col-span-12">
<div class="mr-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 stroke-red-500 fill-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</div>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render["data"].get("error", "No log to show") }}</p>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render.get("data", { "error" : "No log to show" }).get("error", "No log to show") }}</p>
</div>
{% endif %}
{% if pre_render["status"] and pre_render["status"] == "ok" and "error" not in pre_render["data"] %}
{% for key, value in pre_render["data"].items() %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ok" and pre_render.get("data") is mapping and "error" not in pre_render.get("data", {}) %}
{% for key, value in pre_render.get("data", {}).items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">
<div class="core-card-status-container">

View file

@ -10,22 +10,22 @@
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<p class="core-card-text mt-4 mb-2">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#backup-and-restore" class="hover:brightness-90 cursor-pointer mt-2 underline text-primary">in the documentation</a>.</p>
<p class="core-card-text-doc">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#backup-and-restore" class="core-card-text-doc-link">in the documentation</a>.</p>
</div>
<!-- end info -->
{% if pre_render["status"] and pre_render["status"] == "ko" or "error" in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ko" or "error" in pre_render.get("data", {}) or pre_render.get("data") is not mapping %} <div class="core-layout-separator"></div>
<div class="my-2 flex justify-center col-span-12">
<div class="mr-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 stroke-red-500 fill-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</div>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render["data"].get("error", "No log to show") }}</p>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render.get("data", { "error" : "No log to show" }).get("error", "No log to show") }}</p>
</div>
{% endif %}
{% if pre_render["status"] and pre_render["status"] == "ok" and "error" not in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ok" and pre_render.get("data") is mapping and "error" not in pre_render.get("data", {}) %}
<div class="core-card">
<div class="core-card-wrap">
<h5 class="core-card-title">LAST BACKUP</h5>

View file

@ -10,32 +10,32 @@
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<p class="core-card-text mt-4 mb-2">More information available
<a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#bad-behavior" class="hover:brightness-90 cursor-pointer mt-2 underline text-primary">
<p class="core-card-text-doc">More information available
<a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#bad-behavior" class="core-card-text-doc-link">
in the documentation
</a>
.
</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
<!-- end info -->
{% if pre_render["status"] and pre_render["status"] == "ko" or "error" in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ko" or "error" in pre_render.get("data", {}) or pre_render.get("data") is not mapping %} <div class="core-layout-separator"></div>
<div class="my-2 flex justify-center col-span-12">
<div class="mr-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 stroke-red-500 fill-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</div>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render["data"].get("error", "No log to show") }}</p>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render.get("data", { "error" : "No log to show" }).get("error", "No log to show") }}</p>
</div>
{% endif %}
{% if pre_render["status"] and pre_render["status"] == "ok" and "error" not in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ok" and pre_render.get("data") is mapping and "error" not in pre_render.get("data", {}) %}
{% for key, value in pre_render["data"].items() %}
{% for key, value in pre_render.get("data", {}).items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">

View file

@ -10,27 +10,27 @@
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<p class="core-card-text mt-4 mb-2">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#blacklisting" class="hover:brightness-90 cursor-pointer mt-2 underline text-primary">in the documentation</a>.</p>
<p class="core-card-text-doc">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#blacklisting" class="core-card-text-doc-link">in the documentation</a>.</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
<!-- end info -->
{% if pre_render["status"] and pre_render["status"] == "ko" or "error" in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ko" or "error" in pre_render.get("data", {}) or pre_render.get("data") is not mapping %} <div class="core-layout-separator"></div>
<div class="my-2 flex justify-center col-span-12">
<div class="mr-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 stroke-red-500 fill-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</div>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render["data"].get("error", "No log to show") }}</p>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render.get("data", { "error" : "No log to show" }).get("error", "No log to show") }}</p>
</div>
{% endif %}
{% if pre_render["status"] and pre_render["status"] == "ok" and "error" not in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ok" and pre_render.get("data") is mapping and "error" not in pre_render.get("data", {}) %}
<div class="core-layout-separator"></div>
{% for key, value in pre_render["data"].items() %}
{% for key, value in pre_render.get("data", {}).items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">

View file

@ -10,27 +10,27 @@
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<p class="core-card-text mt-4 mb-2">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#bunkernet" class="hover:brightness-90 cursor-pointer mt-2 underline text-primary">in the documentation</a>.</p>
<p class="core-card-text-doc">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#bunkernet" class="core-card-text-doc-link">in the documentation</a>.</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
<!-- end info -->
{% if pre_render["status"] and pre_render["status"] == "ko" or "error" in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ko" or "error" in pre_render.get("data", {}) or pre_render.get("data") is not mapping %} <div class="core-layout-separator"></div>
<div class="my-2 flex justify-center col-span-12">
<div class="mr-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 stroke-red-500 fill-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</div>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render["data"].get("error", "No log to show") }}</p>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render.get("data", { "error" : "No log to show" }).get("error", "No log to show") }}</p>
</div>
{% endif %}
{% if pre_render["status"] and pre_render["status"] == "ok" and "error" not in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ok" and pre_render.get("data") is mapping and "error" not in pre_render.get("data", {}) %}
{% for key, value in pre_render["data"].items() %}
{% for key, value in pre_render.get("data", {}).items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">

View file

@ -10,27 +10,27 @@
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<p class="core-card-text mt-4 mb-2">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#cors" class="hover:brightness-90 cursor-pointer mt-2 underline text-primary">in the documentation</a>.</p>
<p class="core-card-text-doc">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#cors" class="core-card-text-doc-link">in the documentation</a>.</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
<!-- end info -->
{% if pre_render["status"] and pre_render["status"] == "ko" or "error" in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ko" or "error" in pre_render.get("data", {}) or pre_render.get("data") is not mapping %} <div class="core-layout-separator"></div>
<div class="my-2 flex justify-center col-span-12">
<div class="mr-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 stroke-red-500 fill-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</div>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render["data"].get("error", "No log to show") }}</p>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render.get("data", { "error" : "No log to show" }).get("error", "No log to show") }}</p>
</div>
{% endif %}
{% if pre_render["status"] and pre_render["status"] == "ok" and "error" not in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ok" and pre_render.get("data") is mapping and "error" not in pre_render.get("data", {}) %}
{% for key, value in pre_render["data"].items() %}
{% for key, value in pre_render.get("data", {}).items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">

View file

@ -10,27 +10,27 @@
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<p class="core-card-text mt-4 mb-2">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#country" class="hover:brightness-90 cursor-pointer mt-2 underline text-primary">in the documentation</a>.</p>
<p class="core-card-text-doc">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#country" class="core-card-text-doc-link">in the documentation</a>.</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
<!-- end info -->
{% if pre_render["status"] and pre_render["status"] == "ko" or "error" in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ko" or "error" in pre_render.get("data", {}) or pre_render.get("data") is not mapping %} <div class="core-layout-separator"></div>
<div class="my-2 flex justify-center col-span-12">
<div class="mr-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 stroke-red-500 fill-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</div>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render["data"].get("error", "No log to show") }}</p>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render.get("data", { "error" : "No log to show" }).get("error", "No log to show") }}</p>
</div>
{% endif %}
{% if pre_render["status"] and pre_render["status"] == "ok" and "error" not in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ok" and pre_render.get("data") is mapping and "error" not in pre_render.get("data", {}) %}
{% for key, value in pre_render["data"].items() %}
{% for key, value in pre_render.get("data", {}).items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">

View file

@ -10,27 +10,27 @@
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<p class="core-card-text mt-4 mb-2">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#dnsbl" class="hover:brightness-90 cursor-pointer mt-2 underline text-primary">in the documentation</a>.</p>
<p class="core-card-text-doc">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#dnsbl" class="core-card-text-doc-link">in the documentation</a>.</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
<!-- end info -->
{% if pre_render["status"] and pre_render["status"] == "ko" or "error" in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ko" or "error" in pre_render.get("data", {}) or pre_render.get("data") is not mapping %} <div class="core-layout-separator"></div>
<div class="my-2 flex justify-center col-span-12">
<div class="mr-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 stroke-red-500 fill-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</div>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render["data"].get("error", "No log to show") }}</p>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render.get("data", { "error" : "No log to show" }).get("error", "No log to show") }}</p>
</div>
{% endif %}
{% if pre_render["status"] and pre_render["status"] == "ok" and "error" not in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ok" and pre_render.get("data") is mapping and "error" not in pre_render.get("data", {}) %}
{% for key, value in pre_render["data"].items() %}
{% for key, value in pre_render.get("data", {}).items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">

View file

@ -9,27 +9,27 @@
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<p class="core-card-text mt-4 mb-2">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/settings/#errors" class="hover:brightness-90 cursor-pointer mt-2 underline text-primary">in the documentation</a>.</p>
<p class="core-card-text-doc">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/settings/#errors" class="core-card-text-doc-link">in the documentation</a>.</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
<!-- end info -->
{% if pre_render["status"] and pre_render["status"] == "ko" or "error" in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ko" or "error" in pre_render.get("data", {}) or pre_render.get("data") is not mapping %} <div class="core-layout-separator"></div>
<div class="my-2 flex justify-center col-span-12">
<div class="mr-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 stroke-red-500 fill-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</div>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render["data"].get("error", "No log to show") }}</p>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render.get("data", { "error" : "No log to show" }).get("error", "No log to show") }}</p>
</div>
{% endif %}
{% if pre_render["status"] and pre_render["status"] == "ok" and "error" not in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ok" and pre_render.get("data") is mapping and "error" not in pre_render.get("data", {}) %}
{% for key, value in pre_render["data"].items() %}
{% for key, value in pre_render.get("data", {}).items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">

View file

@ -10,27 +10,27 @@
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<p class="core-card-text mt-4 mb-2">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#greylisting" class="hover:brightness-90 cursor-pointer mt-2 underline text-primary">in the documentation</a>.</p>
<p class="core-card-text-doc">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#greylisting" class="core-card-text-doc-link">in the documentation</a>.</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
<!-- end info -->
{% if pre_render["status"] and pre_render["status"] == "ko" or "error" in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ko" or "error" in pre_render.get("data", {}) or pre_render.get("data") is not mapping %} <div class="core-layout-separator"></div>
<div class="my-2 flex justify-center col-span-12">
<div class="mr-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 stroke-red-500 fill-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</div>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render["data"].get("error", "No log to show") }}</p>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render.get("data", { "error" : "No log to show" }).get("error", "No log to show") }}</p>
</div>
{% endif %}
{% if pre_render["status"] and pre_render["status"] == "ok" and "error" not in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ok" and pre_render.get("data") is mapping and "error" not in pre_render.get("data", {}) %}
{% for key, value in pre_render["data"].items() %}
{% for key, value in pre_render.get("data", {}).items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">

View file

@ -10,27 +10,27 @@
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<p class="core-card-text mt-4 mb-2">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#limiting" class="hover:brightness-90 cursor-pointer mt-2 underline text-primary">in the documentation</a>.</p>
<p class="core-card-text-doc">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#limiting" class="core-card-text-doc-link">in the documentation</a>.</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
<!-- end info -->
{% if pre_render["status"] and pre_render["status"] == "ko" or "error" in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ko" or "error" in pre_render.get("data", {}) or pre_render.get("data") is not mapping %} <div class="core-layout-separator"></div>
<div class="my-2 flex justify-center col-span-12">
<div class="mr-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 stroke-red-500 fill-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</div>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render["data"].get("error", "No log to show") }}</p>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render.get("data", { "error" : "No log to show" }).get("error", "No log to show") }}</p>
</div>
{% endif %}
{% if pre_render["status"] and pre_render["status"] == "ok" and "error" not in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ok" and pre_render.get("data") is mapping and "error" not in pre_render.get("data", {}) %}
{% for key, value in pre_render["data"].items() %}
{% for key, value in pre_render.get("data", {}).items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">

View file

@ -10,27 +10,27 @@
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<p class="core-card-text mt-4 mb-2">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/settings/#miscellaneous" class="hover:brightness-90 cursor-pointer mt-2 underline text-primary">in the documentation</a>.</p>
<p class="core-card-text-doc">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/settings/#miscellaneous" class="core-card-text-doc-link">in the documentation</a>.</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
<!-- end info -->
{% if pre_render["status"] and pre_render["status"] == "ko" or "error" in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ko" or "error" in pre_render.get("data", {}) or pre_render.get("data") is not mapping %} <div class="core-layout-separator"></div>
<div class="my-2 flex justify-center col-span-12">
<div class="mr-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 stroke-red-500 fill-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</div>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render["data"].get("error", "No log to show") }}</p>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render.get("data", { "error" : "No log to show" }).get("error", "No log to show") }}</p>
</div>
{% endif %}
{% if pre_render["status"] and pre_render["status"] == "ok" and "error" not in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ok" and pre_render.get("data") is mapping and "error" not in pre_render.get("data", {}) %}
<div class="core-layout-separator"></div>
{% for key, value in pre_render["data"].items() %}
{% for key, value in pre_render.get("data", {}).items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">

View file

@ -96,6 +96,7 @@ def install_plugin(plugin_path: Path, db, preview: bool = True) -> bool:
try:
db = Database(LOGGER, sqlalchemy_string=getenv("DATABASE_URI"))
db_metadata = db.get_metadata()
db_config = db.get_config()
current_date = datetime.now()
pro_license_key = getenv("PRO_LICENSE_KEY", "").strip()
@ -157,7 +158,8 @@ try:
# ? If we already checked today, skip the check and if the metadata is the same, skip the check
if (
metadata.get("is_pro", False) == db_metadata["is_pro"]
pro_license_key == db_config["PRO_LICENSE_KEY"]
and metadata.get("is_pro", False) == db_metadata["is_pro"]
and db_metadata["last_pro_check"]
and current_date.replace(hour=0, minute=0, second=0, microsecond=0) == db_metadata["last_pro_check"].replace(hour=0, minute=0, second=0, microsecond=0)
):

View file

@ -10,27 +10,27 @@
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<p class="core-card-text mt-4 mb-2">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/settings/#redis" class="hover:brightness-90 cursor-pointer mt-2 underline text-primary">in the documentation</a>.</p>
<p class="core-card-text-doc">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/settings/#redis" class="core-card-text-doc-link">in the documentation</a>.</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
<!-- end info -->
{% if pre_render["status"] and pre_render["status"] == "ko" or "error" in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ko" or "error" in pre_render.get("data", {}) or pre_render.get("data") is not mapping %} <div class="core-layout-separator"></div>
<div class="my-2 flex justify-center col-span-12">
<div class="mr-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 stroke-red-500 fill-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</div>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render["data"].get("error", "No log to show") }}</p>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render.get("data", { "error" : "No log to show" }).get("error", "No log to show") }}</p>
</div>
{% endif %}
{% if pre_render["status"] and pre_render["status"] == "ok" and "error" not in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ok" and pre_render.get("data") is mapping and "error" not in pre_render.get("data", {}) %}
{% for key, value in pre_render["data"].items() %}
{% for key, value in pre_render.get("data", {}).items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">

View file

@ -10,27 +10,27 @@
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<p class="core-card-text mt-4 mb-2">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#reverse-scan" class="hover:brightness-90 cursor-pointer mt-2 underline text-primary">in the documentation</a>.</p>
<p class="core-card-text-doc">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#reverse-scan" class="core-card-text-doc-link">in the documentation</a>.</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
<!-- end info -->
{% if pre_render["status"] and pre_render["status"] == "ko" or "error" in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ko" or "error" in pre_render.get("data", {}) or pre_render.get("data") is not mapping %} <div class="core-layout-separator"></div>
<div class="my-2 flex justify-center col-span-12">
<div class="mr-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 stroke-red-500 fill-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</div>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render["data"].get("error", "No log to show") }}</p>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render.get("data", { "error" : "No log to show" }).get("error", "No log to show") }}</p>
</div>
{% endif %}
{% if pre_render["status"] and pre_render["status"] == "ok" and "error" not in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ok" and pre_render.get("data") is mapping and "error" not in pre_render.get("data", {}) %}
{% for key, value in pre_render["data"].items() %}
{% for key, value in pre_render.get("data", {}).items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">

View file

@ -10,27 +10,27 @@
<div class="core-card">
<h5 class="core-card-title">INFO</h5>
<div class="core-card-text-container">
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
<p data-info class="core-card-text">{{plugin.get('description')}}</p>
</div>
<p class="core-card-text mt-4 mb-2">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#whitelisting" class="hover:brightness-90 cursor-pointer mt-2 underline text-primary">in the documentation</a>.</p>
<p class="core-card-text-doc">More information available <a href="https://docs.bunkerweb.io/{{ bw_version }}/security-tuning/#whitelisting" class="core-card-text-doc-link">in the documentation</a>.</p>
</div>
<!-- end info --> <div class="core-layout-separator"></div>
<!-- end info -->
{% if pre_render["status"] and pre_render["status"] == "ko" or "error" in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ko" or "error" in pre_render.get("data", {}) or pre_render.get("data") is not mapping %} <div class="core-layout-separator"></div>
<div class="my-2 flex justify-center col-span-12">
<div class="mr-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 stroke-red-500 fill-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</div>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render["data"].get("error", "No log to show") }}</p>
<p class="px-1 text-white break-words">(Pre rendering error) {{ pre_render.get("data", { "error" : "No log to show" }).get("error", "No log to show") }}</p>
</div>
{% endif %}
{% if pre_render["status"] and pre_render["status"] == "ok" and "error" not in pre_render["data"] %}
{% if pre_render.get("status", False) and pre_render.get("status", False) == "ok" and pre_render.get("data") is mapping and "error" not in pre_render.get("data", {}) %}
{% for key, value in pre_render["data"].items() %}
{% for key, value in pre_render.get("data", {}).items() %}
{% if key.startswith("ping_") %}
<div class="core-card-status">

View file

@ -3,17 +3,17 @@
from contextlib import contextmanager, suppress
from copy import deepcopy
from datetime import datetime
from hashlib import sha256
from inspect import getsourcefile
from io import BytesIO
from logging import Logger
from os import _exit, getenv, listdir, sep
from os.path import basename, join
from os.path import join
from pathlib import Path
from re import compile as re_compile
from sys import _getframe, path as sys_path
from sys import argv, path as sys_path
from typing import Any, Dict, List, Literal, Optional, Tuple, Union
from time import sleep
from traceback import format_exc
from zipfile import ZIP_DEFLATED, ZipFile
from model import (
Base,
@ -37,7 +37,7 @@ for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in ((
if deps_path not in sys_path:
sys_path.append(deps_path)
from common_utils import file_hash # type: ignore
from common_utils import bytes_hash # type: ignore
from pymysql import install_as_MySQLdb
from sqlalchemy import create_engine, event, MetaData as sql_metadata, text, inspect
@ -717,6 +717,7 @@ class Database:
.with_entities(
Plugin_pages.template_checksum,
Plugin_pages.actions_checksum,
Plugin_pages.obfuscation_checksum,
)
.filter_by(plugin_id=plugin["id"])
.first()
@ -728,8 +729,21 @@ class Database:
if {"template.html", "actions.py"}.issubset(listdir(str(path_ui))):
template = path_ui.joinpath("template.html").read_bytes()
actions = path_ui.joinpath("actions.py").read_bytes()
template_checksum = sha256(template).hexdigest()
actions_checksum = sha256(actions).hexdigest()
template_checksum = bytes_hash(template, algorithm="sha256")
actions_checksum = bytes_hash(actions, algorithm="sha256")
obfuscation_file = None
obfuscation_checksum = None
obfuscation_dir = path_ui.joinpath("pyarmor_runtime_000000")
if obfuscation_dir.is_dir():
obfuscation_file = BytesIO()
with ZipFile(obfuscation_file, "w", ZIP_DEFLATED) as zip_file:
for path in obfuscation_dir.rglob("*"):
if path.is_file():
zip_file.write(path, path.relative_to(path_ui))
obfuscation_file.seek(0, 0)
obfuscation_file = obfuscation_file.getvalue()
obfuscation_checksum = bytes_hash(obfuscation_file, algorithm="sha256")
if db_plugin_page:
updates = {}
@ -749,6 +763,14 @@ class Database:
}
)
if obfuscation_checksum != db_plugin_page.obfuscation_checksum:
updates.update(
{
Plugin_pages.obfuscation_file: obfuscation_file,
Plugin_pages.obfuscation_checksum: obfuscation_checksum,
}
)
if updates:
self.logger.warning(f'Page for plugin "{plugin["id"]}" already exists, updating it with the new values')
session.query(Plugin_pages).filter(Plugin_pages.plugin_id == plugin["id"]).update(updates)
@ -764,6 +786,8 @@ class Database:
template_checksum=template_checksum,
actions_file=actions,
actions_checksum=actions_checksum,
obfuscation_file=obfuscation_file,
obfuscation_checksum=obfuscation_checksum,
)
)
remove = False
@ -1103,7 +1127,7 @@ class Database:
custom_config["type"] = custom_config["type"].replace("-", "_").lower() # type: ignore
custom_config["data"] = custom_config["data"].encode("utf-8") if isinstance(custom_config["data"], str) else custom_config["data"]
custom_config["checksum"] = sha256(custom_config["data"]).hexdigest() # type: ignore
custom_config["checksum"] = bytes_hash(custom_config["data"], algorithm="sha256") # type: ignore
service_id = custom_config.get("service_id", None) or None
filters = {
@ -1292,7 +1316,7 @@ class Database:
return ""
def delete_job_cache(self, file_name: str, *, job_name: Optional[str] = None, service_id: Optional[str] = None):
job_name = job_name or basename(getsourcefile(_getframe(1))).replace(".py", "")
job_name = job_name or argv[0].replace(".py", "")
filters = {"file_name": file_name}
if job_name:
filters["job_name"] = job_name
@ -1312,7 +1336,7 @@ class Database:
checksum: Optional[str] = None,
) -> str:
"""Update the plugin cache in the database"""
job_name = job_name or basename(getsourcefile(_getframe(1))).replace(".py", "")
job_name = job_name or argv[0].replace(".py", "")
service_id = service_id or None
with self.__db_session() as session:
cache = session.query(Jobs_cache).filter_by(job_name=job_name, service_id=service_id, file_name=file_name).first()
@ -1578,6 +1602,7 @@ class Database:
.with_entities(
Plugin_pages.template_checksum,
Plugin_pages.actions_checksum,
Plugin_pages.obfuscation_checksum,
)
.filter_by(plugin_id=plugin["id"])
.first()
@ -1587,32 +1612,46 @@ class Database:
if path_ui.is_dir():
remove = True
if {"template.html", "actions.py"}.issubset(listdir(str(path_ui))):
template = path_ui.joinpath("template.html").read_bytes()
actions = path_ui.joinpath("actions.py").read_bytes()
template_checksum = bytes_hash(template, algorithm="sha256")
actions_checksum = bytes_hash(actions, algorithm="sha256")
obfuscation_file = None
obfuscation_checksum = None
obfuscation_dir = path_ui.joinpath("pyarmor_runtime_000000")
if obfuscation_dir.is_dir():
obfuscation_file = BytesIO()
with ZipFile(obfuscation_file, "w", ZIP_DEFLATED) as zip_file:
for path in obfuscation_dir.rglob("*"):
if path.is_file():
zip_file.write(path, path.relative_to(path_ui))
obfuscation_file.seek(0, 0)
obfuscation_file = obfuscation_file.getvalue()
obfuscation_checksum = bytes_hash(obfuscation_file, algorithm="sha256")
if not db_plugin_page:
changes = True
template = path_ui.joinpath("template.html").read_bytes()
actions = path_ui.joinpath("actions.py").read_bytes()
to_put.append(
Plugin_pages(
plugin_id=plugin["id"],
template_file=template,
template_checksum=sha256(template).hexdigest(),
template_checksum=template_checksum,
actions_file=actions,
actions_checksum=sha256(actions).hexdigest(),
actions_checksum=actions_checksum,
obfuscation_file=obfuscation_file,
obfuscation_checksum=obfuscation_checksum,
)
)
remove = False
else:
updates = {}
template_path = path_ui.joinpath("template.html")
actions_path = path_ui.joinpath("actions.py")
template_checksum = file_hash(str(template_path))
actions_checksum = file_hash(str(actions_path))
if template_checksum != db_plugin_page.template_checksum:
updates.update(
{
Plugin_pages.template_file: template_path.read_bytes(),
Plugin_pages.template_file: template,
Plugin_pages.template_checksum: template_checksum,
}
)
@ -1620,11 +1659,19 @@ class Database:
if actions_checksum != db_plugin_page.actions_checksum:
updates.update(
{
Plugin_pages.actions_file: actions_path.read_bytes(),
Plugin_pages.actions_file: actions,
Plugin_pages.actions_checksum: actions_checksum,
}
)
if obfuscation_checksum != db_plugin_page.obfuscation_checksum:
updates.update(
{
Plugin_pages.obfuscation_file: obfuscation_file,
Plugin_pages.obfuscation_checksum: obfuscation_checksum,
}
)
if updates:
changes = True
session.query(Plugin_pages).filter(Plugin_pages.plugin_id == plugin["id"]).update(updates)
@ -1732,35 +1779,49 @@ class Database:
.with_entities(
Plugin_pages.template_checksum,
Plugin_pages.actions_checksum,
Plugin_pages.obfuscation_checksum,
)
.filter_by(plugin_id=plugin["id"])
.first()
)
template = path_ui.joinpath("template.html").read_bytes()
actions = path_ui.joinpath("actions.py").read_bytes()
template_checksum = bytes_hash(template, algorithm="sha256")
actions_checksum = bytes_hash(actions, algorithm="sha256")
obfuscation_file = None
obfuscation_checksum = None
obfuscation_dir = path_ui.joinpath("pyarmor_runtime_000000")
if obfuscation_dir.is_dir():
obfuscation_file = BytesIO()
with ZipFile(obfuscation_file, "w", ZIP_DEFLATED) as zip_file:
for path in obfuscation_dir.rglob("*"):
if path.is_file():
zip_file.write(path, path.relative_to(path_ui))
obfuscation_file.seek(0, 0)
obfuscation_file = obfuscation_file.getvalue()
obfuscation_checksum = bytes_hash(obfuscation_file, algorithm="sha256")
if not db_plugin_page:
template = path_ui.joinpath("template.html").read_bytes()
actions = path_ui.joinpath("actions.py").read_bytes()
to_put.append(
Plugin_pages(
plugin_id=plugin["id"],
template_file=template,
template_checksum=sha256(template).hexdigest(),
template_checksum=template_checksum,
actions_file=actions,
actions_checksum=sha256(actions).hexdigest(),
actions_checksum=actions_checksum,
obfuscation_file=obfuscation_file,
obfuscation_checksum=obfuscation_checksum,
)
)
else:
updates = {}
template_path = path_ui.joinpath("template.html")
actions_path = path_ui.joinpath("actions.py")
template_checksum = file_hash(str(template_path))
actions_checksum = file_hash(str(actions_path))
if template_checksum != db_plugin_page.template_checksum:
updates.update(
{
Plugin_pages.template_file: template_path.read_bytes(),
Plugin_pages.template_file: template,
Plugin_pages.template_checksum: template_checksum,
}
)
@ -1768,15 +1829,23 @@ class Database:
if actions_checksum != db_plugin_page.actions_checksum:
updates.update(
{
Plugin_pages.actions_file: actions_path.read_bytes(),
Plugin_pages.actions_file: actions,
Plugin_pages.actions_checksum: actions_checksum,
}
)
if obfuscation_checksum != db_plugin_page.obfuscation_checksum:
updates.update(
{
Plugin_pages.obfuscation_file: obfuscation_file,
Plugin_pages.obfuscation_checksum: obfuscation_checksum,
}
)
if updates:
session.query(Plugin_pages).filter(Plugin_pages.plugin_id == plugin["id"]).update(updates)
for command, file_name in plugin.get("bwcli", {}).items():
for command, file_name in commands.items():
if not plugin_path.joinpath("bwcli", file_name).is_file():
self.logger.warning(f'Command "{command}"\'s file "{file_name}" does not exist in the plugin directory, skipping it')
continue
@ -1806,7 +1875,7 @@ class Database:
with self.__db_session() as session:
entities = [Plugins.id, Plugins.stream, Plugins.name, Plugins.description, Plugins.version, Plugins.type, Plugins.method, Plugins.checksum]
if with_data:
entities.append(Plugins.data)
entities.append(Plugins.data) # type: ignore
db_plugins = session.query(Plugins).with_entities(*entities)
if _type != "all":
@ -1876,6 +1945,7 @@ class Database:
with self.__db_session() as session:
return {
job.name: {
"plugin_id": job.plugin_id,
"every": job.every,
"reload": job.reload,
"success": job.success,
@ -1898,6 +1968,7 @@ class Database:
for job in (
session.query(Jobs).with_entities(
Jobs.name,
Jobs.plugin_id,
Jobs.every,
Jobs.reload,
Jobs.success,
@ -2069,6 +2140,16 @@ class Database:
return page.template_file
def get_plugin_obfuscation(self, plugin: str) -> Optional[Any]:
"""get obfuscation file for the plugin"""
with self.__db_session() as session:
page = session.query(Plugin_pages).with_entities(Plugin_pages.obfuscation_file).filter_by(plugin_id=plugin).first()
if not page:
return None
return page.obfuscation_file
def get_ui_user(self) -> Optional[dict]:
"""Get ui user."""
with self.__db_session() as session:

View file

@ -150,6 +150,8 @@ class Plugin_pages(Base):
template_checksum = Column(String(128), nullable=False)
actions_file = Column(LargeBinary(length=(2**32) - 1), nullable=False)
actions_checksum = Column(String(128), nullable=False)
obfuscation_file = Column(LargeBinary(length=(2**32) - 1), default=None, nullable=True)
obfuscation_checksum = Column(String(128), default=None, nullable=True)
plugin = relationship("Plugins", back_populates="pages")

View file

@ -151,9 +151,9 @@
},
{
"id": "lua-resty-openssl",
"name": "lua-resty-openssl v1.2.1",
"name": "lua-resty-openssl v1.3.0",
"url": "https://github.com/fffonion/lua-resty-openssl.git",
"commit": "58c6ce6885556ed7cb85dde83d673fad05ba73aa",
"commit": "79b9c2f787febd03b9741ef9fad084a39a888d7c",
"post_install": "rm -r src/deps/src/lua-resty-openssl/t"
},
{

View file

@ -29,14 +29,14 @@ jobs:
# TODO: arm64
# latest and one version older for valgrind and perf test
- nginx: "1.19.9"
openssl: "3.1.4"
openssl: "3.1.5"
openssl_fips: "3.0.8"
extras: "valgrind"
lua_nginx_module: "v0.10.20"
lua_resty_core: "v0.1.22"
nginx_cc_opts: "-Wno-error"
- nginx: "1.21.4"
openssl: "3.1.4"
openssl: "3.1.5"
openssl_fips: "3.0.8"
extras: "valgrind"
lua_nginx_module: "v0.10.25"
@ -48,23 +48,23 @@ jobs:
lua_nginx_module: "v0.10.26"
lua_resty_core: "v0.1.28"
- nginx: "1.25.3"
openssl: "3.0.12"
openssl: "3.0.13"
openssl_fips: "3.0.8"
extras: "valgrind perf"
lua_nginx_module: "v0.10.26"
lua_resty_core: "v0.1.28"
nginx_cc_opts: "-Wno-error"
- nginx: "1.25.3"
openssl: "3.1.4"
openssl: "3.1.5"
openssl_fips: "3.0.8"
extras: "valgrind perf"
lua_nginx_module: "v0.10.26"
lua_resty_core: "v0.1.28"
nginx_cc_opts: "-Wno-error"
- nginx: "1.25.3"
openssl: "3.2.0"
openssl: "3.2.1"
openssl_fips: "3.0.8"
extras: "valgrind perf"
extras: "valgrind perf lua-kong-nginx-module"
lua_nginx_module: "v0.10.26"
lua_resty_core: "v0.1.28"
nginx_cc_opts: "-Wno-error"
@ -115,6 +115,7 @@ jobs:
git clone https://github.com/openresty/lua-nginx-module.git ./lua-nginx-module -b ${{ matrix.lua_nginx_module }}
git clone https://github.com/openresty/no-pool-nginx.git ./no-pool-nginx
git clone https://github.com/fffonion/lua-resty-openssl-aux-module ./lua-resty-openssl-aux-module
git clone https://github.com/Kong/lua-kong-nginx-module.git ./lua-kong-nginx-module
# lua libraries at parent directory of current repository
popd
git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core -b ${{ matrix.lua_resty_core }}
@ -170,15 +171,17 @@ jobs:
- name: Build Nginx
env:
NGINX_CC_OPTS: ${{ matrix.nginx_cc_opts }}
NGINX_MODULES: ""
run: |
if [[ "${{ matrix.extras }}" == *valgrind* ]]; then NGINX_CC_OPTS="$NGINX_CC_OPTS -O0"; fi
if [[ "${{ matrix.extras }}" == *lua-kong-nginx-module* ]]; then NGINX_MODULES="$NGINX_MODULES --add-module=../lua-kong-nginx-module"; fi
export PATH=$BASE_PATH/work/nginx/sbin:$BASE_PATH/../nginx-devel-utils:$PATH
export LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH
export NGX_LUA_LOC=$BASE_PATH/../lua-nginx-module
export NGX_STREAM_LUA_LOC=$BASE_PATH/../stream-lua-nginx-module
export
cd $BASE_PATH
if [ ! -e work ]; then ngx-build ${{ matrix.nginx }} --add-module=../ndk-nginx-module --add-module=../lua-nginx-module --add-module=../lua-resty-openssl-aux-module --with-http_ssl_module --with-cc-opt="-I$OPENSSL_INC $NGINX_CC_OPTS" --with-ld-opt="-L$OPENSSL_LIB -Wl,-rpath,$OPENSSL_LIB" --with-debug > build.log 2>&1 || (cat build.log && exit 1); fi
if [ ! -e work ]; then ngx-build ${{ matrix.nginx }} --add-module=../ndk-nginx-module --add-module=../lua-nginx-module --add-module=../lua-resty-openssl-aux-module $NGINX_MODULES --with-http_ssl_module --with-cc-opt="-I$OPENSSL_INC $NGINX_CC_OPTS" --with-ld-opt="-L$OPENSSL_LIB -Wl,-rpath,$OPENSSL_LIB" --with-debug > build.log 2>&1 || (cat build.log && exit 1); fi
nginx -V
ldd `which nginx`|grep -E 'luajit|ssl|pcre'
@ -191,6 +194,8 @@ jobs:
echo "Nginx SSL plain FFI"
export CI_SKIP_NGINX_C=1
TEST_NGINX_TIMEOUT=10 prove -j$JOBS t/openssl/ssl/ 2>&1
export CI_SKIP_KONG_SSL_FUNCS=1
TEST_NGINX_TIMEOUT=10 prove -j$JOBS t/openssl/ssl/ 2>&1
- name: Run Valgrind
if: contains(matrix.extras, 'valgrind')
@ -205,6 +210,9 @@ jobs:
export CI_SKIP_NGINX_C=1
stdbuf -o 0 -e 0 prove -j$JOBS t/openssl/ssl/ 2>&1 | grep -v "Connection refused" | grep -v "Retry connecting after" | tee output.log
if grep -q 'insert_a_suppression_name_here' output.log; then echo "Valgrind found problems"; exit 1; fi
export CI_SKIP_KONG_SSL_FUNCS=1
stdbuf -o 0 -e 0 prove -j$JOBS t/openssl/ssl/ 2>&1 | grep -v "Connection refused" | grep -v "Retry connecting after" | tee output.log
if grep -q 'insert_a_suppression_name_here' output.log; then echo "Valgrind found problems"; exit 1; fi
- name: Run FIPS Test
run: |

View file

@ -2,6 +2,12 @@
## [Unreleased]
<a name="1.3.0"></a>
## [1.3.0] - 2024-04-15
### features
- **aux/nginx:** use lua-kong-nginx-module's get_socket_ssl when available ([#3](https://github.com/fffonion/lua-resty-openssl/issues/3)) [48c5107](https://github.com/fffonion/lua-resty-openssl/commit/48c51077444e375b2cdd5155693b49d92a82d4a1)
<a name="1.2.1"></a>
## [1.2.1] - 2024-02-27
### bug fixes
@ -488,8 +494,8 @@
- **autogen:** generate tests for x509, csr and crl [1392428](https://github.com/fffonion/lua-resty-openssl/commit/1392428352164d2a1a6e0c03075ff65b55aecdee)
- **objects:** add helper function for ASN1_OBJECT [d037706](https://github.com/fffonion/lua-resty-openssl/commit/d037706c11d716afe3616bdaf4658afc1763081d)
- **pkey:** asymmetric encryption and decryption [6d60451](https://github.com/fffonion/lua-resty-openssl/commit/6d60451157edbf9cefb634f888dfa3e6d9be302f)
- **x509:** getter/setters for extensions [243f40d](https://github.com/fffonion/lua-resty-openssl/commit/243f40d35562a516f404188a5c7eb8f5134d9b30)
- **x509:** add get_ocsp_url and get_crl_url [6141b6f](https://github.com/fffonion/lua-resty-openssl/commit/6141b6f5aed38706b477a71d8c4383bf55da7eee)
- **x509:** getter/setters for extensions [243f40d](https://github.com/fffonion/lua-resty-openssl/commit/243f40d35562a516f404188a5c7eb8f5134d9b30)
- **x509.altname:** support iterate and decode over the stack [083a201](https://github.com/fffonion/lua-resty-openssl/commit/083a201746e02d51f6c5c640ad9bf8c6730ebe0b)
- **x509.crl:** add crl module [242f8cb](https://github.com/fffonion/lua-resty-openssl/commit/242f8cb45d6c2df5918f26540c92a430d42feb5d)
- **x509.csr:** autogen some csr functions as well [9800e36](https://github.com/fffonion/lua-resty-openssl/commit/9800e36c2ff8a299b88f24091cc722940a8652bb)
@ -577,7 +583,8 @@
- **x509:** export pubkey [ede4f81](https://github.com/fffonion/lua-resty-openssl/commit/ede4f817cb0fe092ad6f9ab5d6ecdcde864a9fd8)
[Unreleased]: https://github.com/fffonion/lua-resty-openssl/compare/1.2.1...HEAD
[Unreleased]: https://github.com/fffonion/lua-resty-openssl/compare/1.3.0...HEAD
[1.3.0]: https://github.com/fffonion/lua-resty-openssl/compare/1.2.1...1.3.0
[1.2.1]: https://github.com/fffonion/lua-resty-openssl/compare/1.2.0...1.2.1
[1.2.0]: https://github.com/fffonion/lua-resty-openssl/compare/1.1.0...1.2.0
[1.1.0]: https://github.com/fffonion/lua-resty-openssl/compare/1.0.2...1.1.0

View file

@ -24,7 +24,7 @@ try_require_modules()
local _M = {
_VERSION = '1.2.1',
_VERSION = '1.3.0',
}
function _M.load_modules()

View file

@ -1,5 +1,6 @@
local ffi = require "ffi"
local C = ffi.C
local os = os
local SOCKET_CTX_INDEX = 1
local NGX_OK = ngx.OK
@ -30,6 +31,8 @@ end
local stream_subsystem = false
local get_sock_ssl
if ngx.config.subsystem == "stream" then
stream_subsystem = true
@ -48,10 +51,21 @@ if ngx.config.subsystem == "stream" then
int ngx_stream_lua_resty_openssl_aux_get_socket_ssl_ctx(ngx_stream_lua_socket_tcp_upstream_t *u,
void **_sess);
int ngx_stream_lua_kong_get_socket_ssl(ngx_stream_lua_socket_tcp_upstream_t *u,
void **ssl_conn);
]]
-- sanity test
local _ = C.ngx_stream_lua_resty_openssl_aux_get_request_ssl
local success
if not os.getenv("CI_SKIP_KONG_SSL_FUNCS") then
success, get_sock_ssl = pcall(function() return C.ngx_stream_lua_kong_get_socket_ssl end)
end
if not success or get_sock_ssl == nil then
get_sock_ssl = C.ngx_stream_lua_resty_openssl_aux_get_socket_ssl_ctx
end
else
ffi.cdef [[
typedef struct ngx_http_request_s ngx_http_request_t;
@ -68,10 +82,20 @@ else
int ngx_http_lua_resty_openssl_aux_get_socket_ssl_ctx(ngx_http_lua_socket_tcp_upstream_t *u,
void **_sess);
int ngx_http_lua_kong_ffi_get_socket_ssl(ngx_http_lua_socket_tcp_upstream_t *u,
void **ssl_conn);
]]
-- sanity test
local _ = C.ngx_http_lua_resty_openssl_aux_get_request_ssl
local success
if not os.getenv("CI_SKIP_KONG_SSL_FUNCS") then
success, get_sock_ssl = pcall(function() return C.ngx_http_lua_kong_ffi_get_socket_ssl end)
end
if not success or get_sock_ssl == nil then
get_sock_ssl = C.ngx_http_lua_resty_openssl_aux_get_socket_ssl
end
end
local void_pp = ffi.new("void *[1]")
@ -115,12 +139,7 @@ end
get_socket_ssl = function(sock)
local u = sock[SOCKET_CTX_INDEX]
local ret
if stream_subsystem then
ret = C.ngx_stream_lua_resty_openssl_aux_get_socket_ssl(u, void_pp)
else
ret = C.ngx_http_lua_resty_openssl_aux_get_socket_ssl(u, void_pp)
end
local ret = get_sock_ssl(u, void_pp)
if ret ~= NGX_OK then
return nil, "cannot read u->peer.connection->ssl->connection"
@ -151,4 +170,4 @@ return {
get_req_ssl_ctx = get_req_ssl_ctx,
get_socket_ssl = get_socket_ssl,
get_socket_ssl_ctx = get_socket_ssl_ctx,
}
}

View file

@ -1,8 +1,8 @@
package = "lua-resty-openssl"
version = "1.2.1-1"
version = "1.3.0-1"
source = {
url = "git+https://github.com/fffonion/lua-resty-openssl.git",
tag = "1.2.1"
tag = "1.3.0"
}
description = {
detailed = "FFI-based OpenSSL binding for LuaJIT.",

View file

@ -3,7 +3,7 @@
--license agpl3
--version %VERSION%
--architecture x86_64
--depends bash --depends epel-release --depends python39 --depends 'nginx = 1:1.24.0-1.el8.ngx' --depends libcurl-devel --depends libxml2 --depends yajl --depends lmdb-libs --depends GeoIP-devel --depends file-libs --depends net-tools --depends gd --depends sudo --depends procps --depends lsof --depends brotli --depends openssl --depends libpq --depends mysql --depends postgresql --depends sqlite
--depends bash --depends epel-release --depends python39 --depends 'nginx = 1:1.24.0-1.el8.ngx' --depends libcurl-devel --depends libxml2 --depends yajl --depends lmdb-libs --depends GeoIP-devel --depends file-libs --depends net-tools --depends gd --depends sudo --depends procps --depends lsof --depends brotli --depends openssl --depends libpq --depends mysql --depends postgresql --depends sqlite --depends unzip
--description "BunkerWeb %VERSION% for CentOS Stream 8"
--url "https://www.bunkerweb.io"
--maintainer "Bunkerity <contact at bunkerity dot com>"

View file

@ -3,7 +3,7 @@
--license agpl3
--version %VERSION%
--architecture %ARCH%
--depends bash --depends python3 --depends procps --depends python3-pip --depends 'nginx = 1.24.0-1~bookworm' --depends libcurl4 --depends libgeoip-dev --depends libxml2 --depends libyajl2 --depends libmagic1 --depends net-tools --depends sudo --depends lsof --depends libpq5 --depends libpcre3 --depends libcap2-bin --depends logrotate --depends mariadb-client --depends postgresql-client --depends sqlite3
--depends bash --depends python3 --depends procps --depends python3-pip --depends 'nginx = 1.24.0-1~bookworm' --depends libcurl4 --depends libgeoip-dev --depends libxml2 --depends libyajl2 --depends libmagic1 --depends net-tools --depends sudo --depends lsof --depends libpq5 --depends libpcre3 --depends libcap2-bin --depends logrotate --depends mariadb-client --depends postgresql-client --depends sqlite3 --depends unzip
--description "BunkerWeb %VERSION% for Debian 12"
--url "https://www.bunkerweb.io"
--maintainer "Bunkerity <contact at bunkerity dot com>"

View file

@ -3,7 +3,7 @@
--license agpl3
--version %VERSION%
--architecture %ARCH%
--depends bash --depends python3 --depends 'nginx >= 1:1.24.0' --depends 'nginx < 1:1.25.0' --depends libcurl-devel --depends libxml2 --depends yajl --depends lmdb-libs --depends geoip-devel --depends gd --depends sudo --depends procps --depends lsof --depends nginx-mod-stream --depends pcre --depends libpq --depends libcap --depends openssl --depends logrotate --depends mysql --depends postgresql --depends sqlite3
--depends bash --depends python3 --depends 'nginx >= 1:1.24.0' --depends 'nginx < 1:1.25.0' --depends libcurl-devel --depends libxml2 --depends yajl --depends lmdb-libs --depends geoip-devel --depends gd --depends sudo --depends procps --depends lsof --depends nginx-mod-stream --depends pcre --depends libpq --depends libcap --depends openssl --depends logrotate --depends mysql --depends postgresql --depends sqlite3 --depends unzip
--description "BunkerWeb %VERSION% for Fedora 39"
--url "https://www.bunkerweb.io"
--maintainer "Bunkerity <contact at bunkerity dot com>"

View file

@ -3,7 +3,7 @@
--license agpl3
--version %VERSION%
--architecture %ARCH%
--depends bash --depends python39 --depends 'nginx >= 1:1.24.0' --depends 'nginx < 1:1.25.0' --depends libcurl-devel --depends libxml2 --depends yajl --depends file-libs --depends net-tools --depends gd --depends sudo --depends procps --depends lsof --depends geoip --depends libpq --depends libcap --depends openssl --depends sqlite
--depends bash --depends python39 --depends 'nginx >= 1:1.24.0' --depends 'nginx < 1:1.25.0' --depends libcurl-devel --depends libxml2 --depends yajl --depends file-libs --depends net-tools --depends gd --depends sudo --depends procps --depends lsof --depends geoip --depends libpq --depends libcap --depends openssl --depends sqlite --depends unzip
--description "BunkerWeb %VERSION% for RHEL 8"
--url "https://www.bunkerweb.io"
--maintainer "Bunkerity <contact at bunkerity dot com>"

View file

@ -3,7 +3,7 @@
--license agpl3
--version %VERSION%
--architecture %ARCH%
--depends bash --depends python39 --depends 'nginx >= 1:1.24.0' --depends 'nginx < 1:1.25.0' --depends libcurl-devel --depends libxml2 --depends yajl --depends file-libs --depends net-tools --depends gd --depends sudo --depends procps --depends lsof --depends libmaxminddb --depends libpq --depends libcap --depends openssl --depends mysql --depends postgresql --depends sqlite
--depends bash --depends python39 --depends 'nginx >= 1:1.24.0' --depends 'nginx < 1:1.25.0' --depends libcurl-devel --depends libxml2 --depends yajl --depends file-libs --depends net-tools --depends gd --depends sudo --depends procps --depends lsof --depends libmaxminddb --depends libpq --depends libcap --depends openssl --depends mysql --depends postgresql --depends sqlite --depends unzip
--description "BunkerWeb %VERSION% for RHEL 9"
--url "https://www.bunkerweb.io"
--maintainer "Bunkerity <contact at bunkerity dot com>"

View file

@ -3,7 +3,7 @@
--license agpl3
--version %VERSION%
--architecture %ARCH%
--depends bash --depends python3 --depends python3-pip --depends 'nginx = 1.24.0-1~jammy' --depends libcurl4 --depends libgeoip-dev --depends libxml2 --depends libyajl2 --depends libmagic1 --depends net-tools --depends sudo --depends procps --depends lsof --depends libpq5 --depends libcap2-bin --depends logrotate --depends mariadb-client --depends postgresql-client --depends sqlite3
--depends bash --depends python3 --depends python3-pip --depends 'nginx = 1.24.0-1~jammy' --depends libcurl4 --depends libgeoip-dev --depends libxml2 --depends libyajl2 --depends libmagic1 --depends net-tools --depends sudo --depends procps --depends lsof --depends libpq5 --depends libcap2-bin --depends logrotate --depends mariadb-client --depends postgresql-client --depends sqlite3 --depends unzip
--description "BunkerWeb %VERSION% for Ubuntu 22.04"
--url "https://www.bunkerweb.io"
--maintainer "Bunkerity <contact at bunkerity dot com>"

View file

@ -113,8 +113,10 @@ function start() {
stop_nginx
# Check if we are in slave/master mode
export MASTER_MODE="$(grep "^MASTER_MODE=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
export SLAVE_MODE="$(grep "^SLAVE_MODE=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
MASTER_MODE="$(grep "^MASTER_MODE=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
export MASTER_MODE
SLAVE_MODE="$(grep "^SLAVE_MODE=" /etc/bunkerweb/variables.env | cut -d '=' -f 2)"
export SLAVE_MODE
if [ "$MASTER_MODE" != "yes" ] ; then
# Generate temp conf for jobs and start nginx

View file

@ -46,7 +46,7 @@ COPY --from=builder --chown=0:101 /usr/share/bunkerweb /usr/share/bunkerweb
WORKDIR /usr/share/bunkerweb
# Add scheduler user, drop bwcli, install runtime dependencies, create data folders and set permissions
RUN apk add --no-cache bash libgcc libstdc++ libpq openssl libmagic mariadb-client postgresql-client sqlite && \
RUN apk add --no-cache bash unzip libgcc libstdc++ libpq openssl libmagic mariadb-client postgresql-client sqlite && \
addgroup -g 101 scheduler && \
adduser -h /var/cache/nginx -g scheduler -s /bin/sh -G scheduler -D -H -u 101 scheduler && \
cp helpers/bwcli /usr/bin/ && \

View file

@ -44,7 +44,7 @@ COPY --from=builder --chown=0:101 /usr/share/bunkerweb /usr/share/bunkerweb
WORKDIR /usr/share/bunkerweb
# Add ui user, install runtime dependencies, create data folders and set permissions
RUN apk add --no-cache bash libmagic mariadb-client postgresql-client sqlite && \
RUN apk add --no-cache bash unzip libmagic mariadb-client postgresql-client sqlite && \
addgroup -g 101 ui && \
adduser -h /var/cache/nginx -g ui -s /bin/sh -G ui -D -H -u 101 ui && \
echo "Docker" > INTEGRATION && \

View file

@ -9,6 +9,7 @@ from string import ascii_letters, digits
from sys import path as sys_path, modules as sys_modules
from pathlib import Path
from typing import Union
from uuid import uuid4
for deps_path in [join(sep, "usr", "share", "bunkerweb", *paths) for paths in (("deps", "python"), ("utils",), ("api",), ("db",))]:
if deps_path not in sys_path:
@ -302,18 +303,39 @@ def run_action(plugin: str, function_name: str = ""):
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)
tmp_dir = None
try:
# Try to import the custom plugin
with NamedTemporaryFile(mode="wb", suffix=".py", delete=True) as temp:
temp.write(module)
temp.flush()
temp.seek(0)
loader = SourceFileLoader("actions", temp.name)
if obfuscation:
tmp_dir = Path(sep, "var", "tmp", "bunkerweb", "ui", "action", str(uuid4()))
tmp_dir.mkdir(parents=True, exist_ok=True)
action_file = tmp_dir.joinpath("actions.py")
with ZipFile(BytesIO(obfuscation), "r") as zip_ref:
zip_ref.extractall(tmp_dir)
action_file.write_bytes(module)
sys_path.append(tmp_dir.as_posix())
loader = SourceFileLoader("actions", action_file.as_posix())
actions = loader.load_module()
else:
with NamedTemporaryFile(mode="wb", suffix=".py", delete=True) as temp:
temp.write(module)
temp.flush()
temp.seek(0)
loader = SourceFileLoader("actions", temp.name)
actions = loader.load_module()
except:
if tmp_dir:
sys_path.pop()
rmtree(tmp_dir, ignore_errors=True)
app.logger.exception("An error occurred while importing the plugin")
return {"status": "ko", "code": 500, "message": "An error occurred while importing the plugin, see logs for more details"}
res = None
message = None
try:
# Try to get the custom plugin custom function and call it
@ -332,11 +354,15 @@ def run_action(plugin: str, function_name: str = ""):
finally:
if sbin_nginx_path.is_file():
# Remove the custom plugin from the shared library
if sys_path:
sys_path.pop()
sys_modules.pop("actions", None)
del actions
if tmp_dir:
sys_path.pop()
rmtree(tmp_dir, ignore_errors=True)
if message:
app.logger.exception(message)
if message or not isinstance(res, dict) and not res:
return {"status": "ko", "code": 500, "message": message or "The plugin did not return a valid response"}
@ -401,7 +427,7 @@ def inject_variables():
pro_expire=metadata["pro_expire"].strftime("%d-%m-%Y") if metadata["pro_expire"] else "Unknown",
pro_overlapped=metadata["pro_overlapped"],
plugins=app.config["CONFIG"].get_plugins(),
bw_version="1.5.7"
bw_version=bw_version,
)
@ -572,6 +598,7 @@ def setup():
"REVERSE_PROXY_URL": request.form["ui_url"] or "/",
"AUTO_LETS_ENCRYPT": request.form.get("auto_lets_encrypt", "no"),
"INTERCEPTED_ERROR_CODES": "400 404 405 413 429 500 501 502 503 504",
"MAX_CLIENT_SIZE": "50m",
},
request.form["server_name"],
request.form["server_name"],

View file

@ -3,11 +3,11 @@ beautifulsoup4==4.12.3
Flask==3.0.3
Flask-Login==0.6.3
Flask_WTF==1.2.1
gunicorn[gthread]==21.2.0
gunicorn[gthread]==22.0.0
importlib-metadata==7.1.0
pyotp==2.9.0
python-magic==0.4.27
python_dateutil==2.9.0.post0
qrcode==7.4.2
regex==2023.12.25
regex==2024.4.16
werkzeug==3.0.2

View file

@ -60,9 +60,9 @@ flask-wtf==1.2.1 \
--hash=sha256:8bb269eb9bb46b87e7c8233d7e7debdf1f8b74bf90cc1789988c29b37a97b695 \
--hash=sha256:fa6793f2fb7e812e0fe9743b282118e581fb1b6c45d414b8af05e659bd653287
# via -r requirements.in
gunicorn==21.2.0 \
--hash=sha256:3213aa5e8c24949e792bcacfc176fef362e7aac80b76c56f6b5122bf350722f0 \
--hash=sha256:88ec8bff1d634f98e61b9f65bc4bf3cd918a90806c6f5c48bc5603849ec81033
gunicorn==22.0.0 \
--hash=sha256:350679f91b24062c86e386e198a15438d53a7a8207235a78ba1b53df4c4378d9 \
--hash=sha256:4a0b436239ff76fb33f11c07a16482c521a7e09c1ce3cc293c2330afe01bec63
# via -r requirements.in
importlib-metadata==7.1.0 \
--hash=sha256:30962b96c0c223483ed6cc7280e7f0199feb01a0e40cfae4d4450fc6fab1f570 \
@ -70,9 +70,9 @@ importlib-metadata==7.1.0 \
# via
# -r requirements.in
# flask
itsdangerous==2.1.2 \
--hash=sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44 \
--hash=sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a
itsdangerous==2.2.0 \
--hash=sha256:c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef \
--hash=sha256:e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173
# via
# flask
# flask-wtf
@ -169,100 +169,100 @@ qrcode==7.4.2 \
--hash=sha256:581dca7a029bcb2deef5d01068e39093e80ef00b4a61098a2182eac59d01643a \
--hash=sha256:9dd969454827e127dbd93696b20747239e6d540e082937c90f14ac95b30f5845
# via -r requirements.in
regex==2023.12.25 \
--hash=sha256:0694219a1d54336fd0445ea382d49d36882415c0134ee1e8332afd1529f0baa5 \
--hash=sha256:086dd15e9435b393ae06f96ab69ab2d333f5d65cbe65ca5a3ef0ec9564dfe770 \
--hash=sha256:094ba386bb5c01e54e14434d4caabf6583334090865b23ef58e0424a6286d3dc \
--hash=sha256:09da66917262d9481c719599116c7dc0c321ffcec4b1f510c4f8a066f8768105 \
--hash=sha256:0ecf44ddf9171cd7566ef1768047f6e66975788258b1c6c6ca78098b95cf9a3d \
--hash=sha256:0fda75704357805eb953a3ee15a2b240694a9a514548cd49b3c5124b4e2ad01b \
--hash=sha256:11a963f8e25ab5c61348d090bf1b07f1953929c13bd2309a0662e9ff680763c9 \
--hash=sha256:150c39f5b964e4d7dba46a7962a088fbc91f06e606f023ce57bb347a3b2d4630 \
--hash=sha256:1b9d811f72210fa9306aeb88385b8f8bcef0dfbf3873410413c00aa94c56c2b6 \
--hash=sha256:1e0eabac536b4cc7f57a5f3d095bfa557860ab912f25965e08fe1545e2ed8b4c \
--hash=sha256:22a86d9fff2009302c440b9d799ef2fe322416d2d58fc124b926aa89365ec482 \
--hash=sha256:22f3470f7524b6da61e2020672df2f3063676aff444db1daa283c2ea4ed259d6 \
--hash=sha256:263ef5cc10979837f243950637fffb06e8daed7f1ac1e39d5910fd29929e489a \
--hash=sha256:283fc8eed679758de38fe493b7d7d84a198b558942b03f017b1f94dda8efae80 \
--hash=sha256:29171aa128da69afdf4bde412d5bedc335f2ca8fcfe4489038577d05f16181e5 \
--hash=sha256:298dc6354d414bc921581be85695d18912bea163a8b23cac9a2562bbcd5088b1 \
--hash=sha256:2aae8101919e8aa05ecfe6322b278f41ce2994c4a430303c4cd163fef746e04f \
--hash=sha256:2f4e475a80ecbd15896a976aa0b386c5525d0ed34d5c600b6d3ebac0a67c7ddf \
--hash=sha256:34e4af5b27232f68042aa40a91c3b9bb4da0eeb31b7632e0091afc4310afe6cb \
--hash=sha256:37f8e93a81fc5e5bd8db7e10e62dc64261bcd88f8d7e6640aaebe9bc180d9ce2 \
--hash=sha256:3a17d3ede18f9cedcbe23d2daa8a2cd6f59fe2bf082c567e43083bba3fb00347 \
--hash=sha256:3b1de218d5375cd6ac4b5493e0b9f3df2be331e86520f23382f216c137913d20 \
--hash=sha256:43f7cd5754d02a56ae4ebb91b33461dc67be8e3e0153f593c509e21d219c5060 \
--hash=sha256:4558410b7a5607a645e9804a3e9dd509af12fb72b9825b13791a37cd417d73a5 \
--hash=sha256:4719bb05094d7d8563a450cf8738d2e1061420f79cfcc1fa7f0a44744c4d8f73 \
--hash=sha256:4bfc2b16e3ba8850e0e262467275dd4d62f0d045e0e9eda2bc65078c0110a11f \
--hash=sha256:518440c991f514331f4850a63560321f833979d145d7d81186dbe2f19e27ae3d \
--hash=sha256:51f4b32f793812714fd5307222a7f77e739b9bc566dc94a18126aba3b92b98a3 \
--hash=sha256:531ac6cf22b53e0696f8e1d56ce2396311254eb806111ddd3922c9d937151dae \
--hash=sha256:5cd05d0f57846d8ba4b71d9c00f6f37d6b97d5e5ef8b3c3840426a475c8f70f4 \
--hash=sha256:5dd58946bce44b53b06d94aa95560d0b243eb2fe64227cba50017a8d8b3cd3e2 \
--hash=sha256:60080bb3d8617d96f0fb7e19796384cc2467447ef1c491694850ebd3670bc457 \
--hash=sha256:636ba0a77de609d6510235b7f0e77ec494d2657108f777e8765efc060094c98c \
--hash=sha256:67d3ccfc590e5e7197750fcb3a2915b416a53e2de847a728cfa60141054123d4 \
--hash=sha256:68191f80a9bad283432385961d9efe09d783bcd36ed35a60fb1ff3f1ec2efe87 \
--hash=sha256:7502534e55c7c36c0978c91ba6f61703faf7ce733715ca48f499d3dbbd7657e0 \
--hash=sha256:7aa47c2e9ea33a4a2a05f40fcd3ea36d73853a2aae7b4feab6fc85f8bf2c9704 \
--hash=sha256:7d2af3f6b8419661a0c421584cfe8aaec1c0e435ce7e47ee2a97e344b98f794f \
--hash=sha256:7e316026cc1095f2a3e8cc012822c99f413b702eaa2ca5408a513609488cb62f \
--hash=sha256:88ad44e220e22b63b0f8f81f007e8abbb92874d8ced66f32571ef8beb0643b2b \
--hash=sha256:88d1f7bef20c721359d8675f7d9f8e414ec5003d8f642fdfd8087777ff7f94b5 \
--hash=sha256:89723d2112697feaa320c9d351e5f5e7b841e83f8b143dba8e2d2b5f04e10923 \
--hash=sha256:8a0ccf52bb37d1a700375a6b395bff5dd15c50acb745f7db30415bae3c2b0715 \
--hash=sha256:8c2c19dae8a3eb0ea45a8448356ed561be843b13cbc34b840922ddf565498c1c \
--hash=sha256:905466ad1702ed4acfd67a902af50b8db1feeb9781436372261808df7a2a7bca \
--hash=sha256:9852b76ab558e45b20bf1893b59af64a28bd3820b0c2efc80e0a70a4a3ea51c1 \
--hash=sha256:98a2636994f943b871786c9e82bfe7883ecdaba2ef5df54e1450fa9869d1f756 \
--hash=sha256:9aa1a67bbf0f957bbe096375887b2505f5d8ae16bf04488e8b0f334c36e31360 \
--hash=sha256:9eda5f7a50141291beda3edd00abc2d4a5b16c29c92daf8d5bd76934150f3edc \
--hash=sha256:a6d1047952c0b8104a1d371f88f4ab62e6275567d4458c1e26e9627ad489b445 \
--hash=sha256:a9b6d73353f777630626f403b0652055ebfe8ff142a44ec2cf18ae470395766e \
--hash=sha256:a9cc99d6946d750eb75827cb53c4371b8b0fe89c733a94b1573c9dd16ea6c9e4 \
--hash=sha256:ad83e7545b4ab69216cef4cc47e344d19622e28aabec61574b20257c65466d6a \
--hash=sha256:b014333bd0217ad3d54c143de9d4b9a3ca1c5a29a6d0d554952ea071cff0f1f8 \
--hash=sha256:b43523d7bc2abd757119dbfb38af91b5735eea45537ec6ec3a5ec3f9562a1c53 \
--hash=sha256:b521dcecebc5b978b447f0f69b5b7f3840eac454862270406a39837ffae4e697 \
--hash=sha256:b77e27b79448e34c2c51c09836033056a0547aa360c45eeeb67803da7b0eedaf \
--hash=sha256:b7a635871143661feccce3979e1727c4e094f2bdfd3ec4b90dfd4f16f571a87a \
--hash=sha256:b7fca9205b59c1a3d5031f7e64ed627a1074730a51c2a80e97653e3e9fa0d415 \
--hash=sha256:ba1b30765a55acf15dce3f364e4928b80858fa8f979ad41f862358939bdd1f2f \
--hash=sha256:ba99d8077424501b9616b43a2d208095746fb1284fc5ba490139651f971d39d9 \
--hash=sha256:c25a8ad70e716f96e13a637802813f65d8a6760ef48672aa3502f4c24ea8b400 \
--hash=sha256:c3c4a78615b7762740531c27cf46e2f388d8d727d0c0c739e72048beb26c8a9d \
--hash=sha256:c40281f7d70baf6e0db0c2f7472b31609f5bc2748fe7275ea65a0b4601d9b392 \
--hash=sha256:c7ad32824b7f02bb3c9f80306d405a1d9b7bb89362d68b3c5a9be53836caebdb \
--hash=sha256:cb3fe77aec8f1995611f966d0c656fdce398317f850d0e6e7aebdfe61f40e1cd \
--hash=sha256:cc038b2d8b1470364b1888a98fd22d616fba2b6309c5b5f181ad4483e0017861 \
--hash=sha256:cc37b9aeebab425f11f27e5e9e6cf580be7206c6582a64467a14dda211abc232 \
--hash=sha256:cc6bb9aa69aacf0f6032c307da718f61a40cf970849e471254e0e91c56ffca95 \
--hash=sha256:d126361607b33c4eb7b36debc173bf25d7805847346dd4d99b5499e1fef52bc7 \
--hash=sha256:d15b274f9e15b1a0b7a45d2ac86d1f634d983ca40d6b886721626c47a400bf39 \
--hash=sha256:d166eafc19f4718df38887b2bbe1467a4f74a9830e8605089ea7a30dd4da8887 \
--hash=sha256:d498eea3f581fbe1b34b59c697512a8baef88212f92e4c7830fcc1499f5b45a5 \
--hash=sha256:d6f7e255e5fa94642a0724e35406e6cb7001c09d476ab5fce002f652b36d0c39 \
--hash=sha256:d78bd484930c1da2b9679290a41cdb25cc127d783768a0369d6b449e72f88beb \
--hash=sha256:d865984b3f71f6d0af64d0d88f5733521698f6c16f445bb09ce746c92c97c586 \
--hash=sha256:d902a43085a308cef32c0d3aea962524b725403fd9373dea18110904003bac97 \
--hash=sha256:d94a1db462d5690ebf6ae86d11c5e420042b9898af5dcf278bd97d6bda065423 \
--hash=sha256:da695d75ac97cb1cd725adac136d25ca687da4536154cdc2815f576e4da11c69 \
--hash=sha256:db2a0b1857f18b11e3b0e54ddfefc96af46b0896fb678c85f63fb8c37518b3e7 \
--hash=sha256:df26481f0c7a3f8739fecb3e81bc9da3fcfae34d6c094563b9d4670b047312e1 \
--hash=sha256:e14b73607d6231f3cc4622809c196b540a6a44e903bcfad940779c80dffa7be7 \
--hash=sha256:e2610e9406d3b0073636a3a2e80db05a02f0c3169b5632022b4e81c0364bcda5 \
--hash=sha256:e692296c4cc2873967771345a876bcfc1c547e8dd695c6b89342488b0ea55cd8 \
--hash=sha256:e693e233ac92ba83a87024e1d32b5f9ab15ca55ddd916d878146f4e3406b5c91 \
--hash=sha256:e81469f7d01efed9b53740aedd26085f20d49da65f9c1f41e822a33992cb1590 \
--hash=sha256:e8c7e08bb566de4faaf11984af13f6bcf6a08f327b13631d41d62592681d24fe \
--hash=sha256:ed19b3a05ae0c97dd8f75a5d8f21f7723a8c33bbc555da6bbe1f96c470139d3c \
--hash=sha256:efb2d82f33b2212898f1659fb1c2e9ac30493ac41e4d53123da374c3b5541e64 \
--hash=sha256:f44dd4d68697559d007462b0a3a1d9acd61d97072b71f6d1968daef26bc744bd \
--hash=sha256:f72cbae7f6b01591f90814250e636065850c5926751af02bb48da94dfced7baa \
--hash=sha256:f7bc09bc9c29ebead055bcba136a67378f03d66bf359e87d0f7c759d6d4ffa31 \
--hash=sha256:ff100b203092af77d1a5a7abe085b3506b7eaaf9abf65b73b7d6905b6cb76988
regex==2024.4.16 \
--hash=sha256:00169caa125f35d1bca6045d65a662af0202704489fada95346cfa092ec23f39 \
--hash=sha256:03576e3a423d19dda13e55598f0fd507b5d660d42c51b02df4e0d97824fdcae3 \
--hash=sha256:03e68f44340528111067cecf12721c3df4811c67268b897fbe695c95f860ac42 \
--hash=sha256:0534b034fba6101611968fae8e856c1698da97ce2efb5c2b895fc8b9e23a5834 \
--hash=sha256:08dea89f859c3df48a440dbdcd7b7155bc675f2fa2ec8c521d02dc69e877db70 \
--hash=sha256:0a38d151e2cdd66d16dab550c22f9521ba79761423b87c01dae0a6e9add79c0d \
--hash=sha256:0c8290b44d8b0af4e77048646c10c6e3aa583c1ca67f3b5ffb6e06cf0c6f0f89 \
--hash=sha256:10188fe732dec829c7acca7422cdd1bf57d853c7199d5a9e96bb4d40db239c73 \
--hash=sha256:1210365faba7c2150451eb78ec5687871c796b0f1fa701bfd2a4a25420482d26 \
--hash=sha256:12f6a3f2f58bb7344751919a1876ee1b976fe08b9ffccb4bbea66f26af6017b9 \
--hash=sha256:159dc4e59a159cb8e4e8f8961eb1fa5d58f93cb1acd1701d8aff38d45e1a84a6 \
--hash=sha256:20b7a68444f536365af42a75ccecb7ab41a896a04acf58432db9e206f4e525d6 \
--hash=sha256:23cff1b267038501b179ccbbd74a821ac4a7192a1852d1d558e562b507d46013 \
--hash=sha256:2c72608e70f053643437bd2be0608f7f1c46d4022e4104d76826f0839199347a \
--hash=sha256:3399dd8a7495bbb2bacd59b84840eef9057826c664472e86c91d675d007137f5 \
--hash=sha256:34422d5a69a60b7e9a07a690094e824b66f5ddc662a5fc600d65b7c174a05f04 \
--hash=sha256:370c68dc5570b394cbaadff50e64d705f64debed30573e5c313c360689b6aadc \
--hash=sha256:3a1018e97aeb24e4f939afcd88211ace472ba566efc5bdf53fd8fd7f41fa7170 \
--hash=sha256:3d5ac5234fb5053850d79dd8eb1015cb0d7d9ed951fa37aa9e6249a19aa4f336 \
--hash=sha256:4313ab9bf6a81206c8ac28fdfcddc0435299dc88cad12cc6305fd0e78b81f9e4 \
--hash=sha256:445ca8d3c5a01309633a0c9db57150312a181146315693273e35d936472df912 \
--hash=sha256:479595a4fbe9ed8f8f72c59717e8cf222da2e4c07b6ae5b65411e6302af9708e \
--hash=sha256:4918fd5f8b43aa7ec031e0fef1ee02deb80b6afd49c85f0790be1dc4ce34cb50 \
--hash=sha256:4aba818dcc7263852aabb172ec27b71d2abca02a593b95fa79351b2774eb1d2b \
--hash=sha256:4e819a806420bc010489f4e741b3036071aba209f2e0989d4750b08b12a9343f \
--hash=sha256:4facc913e10bdba42ec0aee76d029aedda628161a7ce4116b16680a0413f658a \
--hash=sha256:549c3584993772e25f02d0656ac48abdda73169fe347263948cf2b1cead622f3 \
--hash=sha256:5c02fcd2bf45162280613d2e4a1ca3ac558ff921ae4e308ecb307650d3a6ee51 \
--hash=sha256:5f580c651a72b75c39e311343fe6875d6f58cf51c471a97f15a938d9fe4e0d37 \
--hash=sha256:62120ed0de69b3649cc68e2965376048793f466c5a6c4370fb27c16c1beac22d \
--hash=sha256:6295004b2dd37b0835ea5c14a33e00e8cfa3c4add4d587b77287825f3418d310 \
--hash=sha256:65436dce9fdc0aeeb0a0effe0839cb3d6a05f45aa45a4d9f9c60989beca78b9c \
--hash=sha256:684008ec44ad275832a5a152f6e764bbe1914bea10968017b6feaecdad5736e0 \
--hash=sha256:684e52023aec43bdf0250e843e1fdd6febbe831bd9d52da72333fa201aaa2335 \
--hash=sha256:6cc38067209354e16c5609b66285af17a2863a47585bcf75285cab33d4c3b8df \
--hash=sha256:6f2f017c5be19984fbbf55f8af6caba25e62c71293213f044da3ada7091a4455 \
--hash=sha256:743deffdf3b3481da32e8a96887e2aa945ec6685af1cfe2bcc292638c9ba2f48 \
--hash=sha256:7571f19f4a3fd00af9341c7801d1ad1967fc9c3f5e62402683047e7166b9f2b4 \
--hash=sha256:7731728b6568fc286d86745f27f07266de49603a6fdc4d19c87e8c247be452af \
--hash=sha256:785c071c982dce54d44ea0b79cd6dfafddeccdd98cfa5f7b86ef69b381b457d9 \
--hash=sha256:78fddb22b9ef810b63ef341c9fcf6455232d97cfe03938cbc29e2672c436670e \
--hash=sha256:7bb966fdd9217e53abf824f437a5a2d643a38d4fd5fd0ca711b9da683d452969 \
--hash=sha256:7cbc5d9e8a1781e7be17da67b92580d6ce4dcef5819c1b1b89f49d9678cc278c \
--hash=sha256:803b8905b52de78b173d3c1e83df0efb929621e7b7c5766c0843704d5332682f \
--hash=sha256:80b696e8972b81edf0af2a259e1b2a4a661f818fae22e5fa4fa1a995fb4a40fd \
--hash=sha256:81500ed5af2090b4a9157a59dbc89873a25c33db1bb9a8cf123837dcc9765047 \
--hash=sha256:89ec7f2c08937421bbbb8b48c54096fa4f88347946d4747021ad85f1b3021b3c \
--hash=sha256:8ba6745440b9a27336443b0c285d705ce73adb9ec90e2f2004c64d95ab5a7598 \
--hash=sha256:8c91e1763696c0eb66340c4df98623c2d4e77d0746b8f8f2bee2c6883fd1fe18 \
--hash=sha256:8d015604ee6204e76569d2f44e5a210728fa917115bef0d102f4107e622b08d5 \
--hash=sha256:8d1f86f3f4e2388aa3310b50694ac44daefbd1681def26b4519bd050a398dc5a \
--hash=sha256:8f83b6fd3dc3ba94d2b22717f9c8b8512354fd95221ac661784df2769ea9bba9 \
--hash=sha256:8fc6976a3395fe4d1fbeb984adaa8ec652a1e12f36b56ec8c236e5117b585427 \
--hash=sha256:904c883cf10a975b02ab3478bce652f0f5346a2c28d0a8521d97bb23c323cc8b \
--hash=sha256:911742856ce98d879acbea33fcc03c1d8dc1106234c5e7d068932c945db209c0 \
--hash=sha256:91797b98f5e34b6a49f54be33f72e2fb658018ae532be2f79f7c63b4ae225145 \
--hash=sha256:95399831a206211d6bc40224af1c635cb8790ddd5c7493e0bd03b85711076a53 \
--hash=sha256:956b58d692f235cfbf5b4f3abd6d99bf102f161ccfe20d2fd0904f51c72c4c66 \
--hash=sha256:98c1165f3809ce7774f05cb74e5408cd3aa93ee8573ae959a97a53db3ca3180d \
--hash=sha256:9ab40412f8cd6f615bfedea40c8bf0407d41bf83b96f6fc9ff34976d6b7037fd \
--hash=sha256:9df1bfef97db938469ef0a7354b2d591a2d438bc497b2c489471bec0e6baf7c4 \
--hash=sha256:a01fe2305e6232ef3e8f40bfc0f0f3a04def9aab514910fa4203bafbc0bb4682 \
--hash=sha256:a70b51f55fd954d1f194271695821dd62054d949efd6368d8be64edd37f55c86 \
--hash=sha256:a7ccdd1c4a3472a7533b0a7aa9ee34c9a2bef859ba86deec07aff2ad7e0c3b94 \
--hash=sha256:b340cccad138ecb363324aa26893963dcabb02bb25e440ebdf42e30963f1a4e0 \
--hash=sha256:b74586dd0b039c62416034f811d7ee62810174bb70dffcca6439f5236249eb09 \
--hash=sha256:b9d320b3bf82a39f248769fc7f188e00f93526cc0fe739cfa197868633d44701 \
--hash=sha256:ba2336d6548dee3117520545cfe44dc28a250aa091f8281d28804aa8d707d93d \
--hash=sha256:ba8122e3bb94ecda29a8de4cf889f600171424ea586847aa92c334772d200331 \
--hash=sha256:bd727ad276bb91928879f3aa6396c9a1d34e5e180dce40578421a691eeb77f47 \
--hash=sha256:c21fc21a4c7480479d12fd8e679b699f744f76bb05f53a1d14182b31f55aac76 \
--hash=sha256:c2d0e7cbb6341e830adcbfa2479fdeebbfbb328f11edd6b5675674e7a1e37730 \
--hash=sha256:c2ef6f7990b6e8758fe48ad08f7e2f66c8f11dc66e24093304b87cae9037bb4a \
--hash=sha256:c4ed75ea6892a56896d78f11006161eea52c45a14994794bcfa1654430984b22 \
--hash=sha256:cccc79a9be9b64c881f18305a7c715ba199e471a3973faeb7ba84172abb3f317 \
--hash=sha256:d0800631e565c47520aaa04ae38b96abc5196fe8b4aa9bd864445bd2b5848a7a \
--hash=sha256:d2da13568eff02b30fd54fccd1e042a70fe920d816616fda4bf54ec705668d81 \
--hash=sha256:d61ae114d2a2311f61d90c2ef1358518e8f05eafda76eaf9c772a077e0b465ec \
--hash=sha256:d83c2bc678453646f1a18f8db1e927a2d3f4935031b9ad8a76e56760461105dd \
--hash=sha256:dd5acc0a7d38fdc7a3a6fd3ad14c880819008ecb3379626e56b163165162cc46 \
--hash=sha256:df79012ebf6f4efb8d307b1328226aef24ca446b3ff8d0e30202d7ebcb977a8c \
--hash=sha256:e0a2df336d1135a0b3a67f3bbf78a75f69562c1199ed9935372b82215cddd6e2 \
--hash=sha256:e2f142b45c6fed48166faeb4303b4b58c9fcd827da63f4cf0a123c3480ae11fb \
--hash=sha256:e697e1c0238133589e00c244a8b676bc2cfc3ab4961318d902040d099fec7483 \
--hash=sha256:e757d475953269fbf4b441207bb7dbdd1c43180711b6208e129b637792ac0b93 \
--hash=sha256:e87ab229332ceb127a165612d839ab87795972102cb9830e5f12b8c9a5c1b508 \
--hash=sha256:ea355eb43b11764cf799dda62c658c4d2fdb16af41f59bb1ccfec517b60bcb07 \
--hash=sha256:ec7e0043b91115f427998febaa2beb82c82df708168b35ece3accb610b91fac1 \
--hash=sha256:eeaa0b5328b785abc344acc6241cffde50dc394a0644a968add75fcefe15b9d4 \
--hash=sha256:f2d80a6749724b37853ece57988b39c4e79d2b5fe2869a86e8aeae3bbeef9eb0 \
--hash=sha256:fa454d26f2e87ad661c4f0c5a5fe4cf6aab1e307d1b94f16ffdfcb089ba685c0 \
--hash=sha256:fb83cc090eac63c006871fd24db5e30a1f282faa46328572661c0a24a2323a08 \
--hash=sha256:fd80d1280d473500d8086d104962a82d77bfbf2b118053824b7be28cd5a79ea5
# via -r requirements.in
six==1.16.0 \
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \

File diff suppressed because one or more lines are too long

View file

@ -234,7 +234,7 @@ class darkMode {
body: JSON.stringify({ darkmode: isDark }),
};
const send = await fetch(
`${location.href.split("/").slice(0, -1).join("/")}/darkmode`,
document.querySelector("[data-mode-link]").getAttribute("data-mode-link"),
data,
);
}

View file

@ -360,17 +360,18 @@ class Download {
.hasAttribute(`data-${this.prefix}-download`)
) {
const btnEl = e.target.closest("button");
const pluginId = btnEl.getAttribute("data-jobs-plugin");
const jobName = btnEl.getAttribute("data-jobs-download");
const fileName = btnEl.getAttribute("data-jobs-file");
this.sendFileToDL(jobName, fileName);
this.sendFileToDL(pluginId, jobName, fileName);
}
} catch (err) {}
});
}
async sendFileToDL(jobName, fileName) {
async sendFileToDL(pluginId, jobName, fileName) {
window.open(
`${location.href}/download?job_name=${jobName}&file_name=${fileName}`,
`${location.href}/download?plugin_id=${pluginId}&job_name=${jobName}&file_name=${fileName}`,
);
}
}

View file

@ -1,11 +1,13 @@
class Ping {
constructor(
url = `${location.origin}${location.pathname}`,
statusTextEl = null,
statusColorEl = null,
key_to_check = "ping",
btnEl = null, // disabled while fethching
statusTextEl = null, // update text with fetching result
statusColorEl = null, // update color with fetching result
key_to_check = "ping", // key to check in response data
) {
this.url = url;
this.btnEl = btnEl;
this.statusColorEl = statusColorEl;
this.statusTextEl = statusTextEl;
this.key_to_check = key_to_check;
@ -82,6 +84,30 @@ class Ping {
// Key of fetch data need to match key of this.data
updateEl(data) {
// Show error
if (data?.error) {
const error = data?.error || "Action exception, no details available";
console.log(error);
// Remove previous data-action-error
const prevError = document.querySelectorAll("[data-action-error]");
if (prevError.length) prevError.forEach((el) => el.remove());
// Add this one
const error_html = `<div data-action-error class="core-layout-separator"></div>
<div data-action-error class="my-2 flex justify-center col-span-12">
<div class="mr-1">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6 stroke-red-500 fill-white">
<path stroke-linecap="round" stroke-linejoin="round" d="m9.75 9.75 4.5 4.5m0-4.5-4.5 4.5M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
</div>
<p class="px-1 text-white break-words">(Action error) ${error}</p>
</div>
`;
// add HTML at the end of .core-layout
document
.querySelector("div.core-layout")
.insertAdjacentHTML("beforeend", error_html);
}
try {
const successValues = [
"success",
@ -141,8 +167,14 @@ class Ping {
this.alertEl.classList.remove("hidden");
if (type !== "fetch")
if (type === "fetch") {
this.btnEl.setAttribute("disabled", "disabled");
}
if (type !== "fetch") {
this.btnEl.removeAttribute("disabled");
setTimeout(() => this.alertEl.classList.add("hidden"), 5000);
}
}
getAlertType(type) {

View file

@ -57,6 +57,24 @@ class Select {
}
init() {
// Add event listener to close dropdown if scroll event is triggered on window
window.addEventListener("scroll", () => {
this.dropsToHide = document.querySelectorAll(
'[data-setting-select-dropdown][class*="flex"]',
);
if (!this.dropsToHide.length) return;
this.dropsToHide.forEach((dropdown) => {
const btn = dropdown
.closest("div[data-setting-container]")
.querySelector("button[data-setting-select]");
if (dropdown.classList.contains("hidden")) return;
btn.click();
// Add dropdown to remove list
});
});
window.addEventListener("click", (e) => {
//CASE NO BTN SELECT CLICKED
try {
@ -164,6 +182,28 @@ class Select {
dropdownEl.classList.toggle("hidden");
dropdownEl.classList.toggle("flex");
dropdownChevron.classList.toggle("rotate-180");
// case open, we want to move dropdown position next to his data-select-container
if (!dropdownEl.classList.contains("hidden")) {
const selectContainer = btn.closest("div[data-select-container]");
const selectContainerRect = selectContainer.getBoundingClientRect();
const top = selectContainerRect.top + selectContainerRect.height;
const left = selectContainerRect.left;
const width = selectContainerRect.width;
dropdownEl.style.top = `${top}px`;
dropdownEl.style.left = `${left}px`;
dropdownEl.style.width = `${width}px`;
// Check dropdown height, if out of screen, move it up
const dropdownRect = dropdownEl.getBoundingClientRect();
const dropdownHeight = dropdownRect.height;
const dropdownBottom = dropdownRect.bottom;
const windowHeight = window.innerHeight;
if (dropdownBottom > windowHeight) {
dropdownEl.style.top = `${
top - dropdownHeight - selectContainerRect.height - 15
}px`;
}
}
}
}

View file

@ -220,13 +220,19 @@ class TabsSelect {
);
const combobox = dropdown.querySelector("[data-combobox]");
if (combobox) {
// simulate clear combobox wit keyboard
// simulate clear combobox with keyboard
combobox.value = "";
}
dropdown.classList.toggle("hidden");
dropdown.classList.toggle("flex");
// Case open, try to focus on combobox input
if (!dropdown.classList.contains("hidden") && combobox) {
// Unless already input focused (avoid conflict with search)
if (
!dropdown.classList.contains("hidden") &&
combobox &&
combobox.getAttribute("data-focus") !== "false"
) {
combobox.focus();
}
@ -288,6 +294,9 @@ class FilterSettings {
this.tabsEls = this.tabContainer.querySelectorAll(
`[data-tab-select-handler]`,
);
this.comboboxEl = this.tabContainer
.querySelector("[data-tab-select-dropdown]")
.querySelector("[data-combobox]");
this.init();
}
@ -300,11 +309,13 @@ class FilterSettings {
});
}
// Update plugin items based on current input
if (this.comboboxEl) {
this.comboboxEl.addEventListener("input", () => {
this.runComboFilter();
});
// Allow to run combobox filter when opening dropdown (because reset and focus on open)
this.comboboxEl.addEventListener("focusin", () => {
this.runComboFilter();
});
@ -352,7 +363,13 @@ class FilterSettings {
}
runFilter() {
// Reset previous state to start fresh
this.resetFilter();
// get current tab, this will be used to show other plugin tab if current is hidden after filter
const tabNameBeforeFilter =
this.tabContainer
?.querySelector("[data-tab-select-dropdown-btn]")
?.getAttribute("data-tab-id") || "";
//get inp format
const inpValue = this.input.value.trim().toLowerCase().replaceAll("_", " ");
@ -612,37 +629,74 @@ class FilterSettings {
// case no tab match
if (isAllHidden) {
// we want to show message "No match"
this.tabContainer
.querySelector("[data-tab-select-dropdown-btn]")
.setAttribute("data-tab-id", "no-match");
return (this.tabContainer.querySelector(
this.tabContainer.querySelector(
"[data-tab-select-dropdown-btn] span",
).textContent = "No match");
).textContent = "No match";
// we want to close dropdown in case open previously
this.toggleDropdown(true, true, false);
return;
}
// click first not hidden tab
const currTabEl = this.tabContainer.querySelector(
// case at least one match
const currTabBtn = this.tabContainer.querySelector(
`[data-tab-select-handler='${tabNameBeforeFilter}']`,
);
// case the previous plugin is still visible, set is as active by clicking it again
if (currTabBtn && !currTabBtn.classList.contains("!hidden")) {
currTabBtn.click();
}
// case the previous plugin is hidden, click on the first not hidden tab
if (currTabBtn?.classList?.contains("!hidden") || !currTabBtn) {
firstNotHiddenEl.click();
}
// furthermore, open dropdown so user can see remain plugins in case the first one is not the one he is looking for
// and if more than one plugin available
// but we want to avoid dropdown open if active element is input keyword and value is empty
if (document.activeElement === this.input && this.input.value === "")
return;
const hiddenTabsEl = this.tabContainer.querySelectorAll(
`[data-tab-select-handler][class*="!hidden"]`,
);
if (hiddenTabsEl.length < this.tabsEls.length - 1)
this.toggleDropdown(true, false, true);
return;
}
toggleDropdown(
avoidComboFocus = false,
disableOpen = false,
disableClose = false,
) {
// avoid this on mobile
if (window.innerWidth < 768) return;
const dropdownEl = this.tabContainer.querySelector(
"[data-tab-select-dropdown]",
);
const dropdownBtn = this.tabContainer.querySelector(
"[data-tab-select-dropdown-btn]",
);
const currTabName = currTabEl.getAttribute("data-tab-id");
// case previously no match
if (currTabName === "no-match" && !isAllHidden) {
return firstNotHiddenEl.click();
}
const currTabBtn = this.tabContainer.querySelector(
`[data-tab-select-handler='${currTabName}']`,
);
if (!currTabBtn.classList.contains("!hidden")) {
return currTabBtn.click();
}
if (currTabBtn.classList.contains("!hidden")) {
return firstNotHiddenEl.click();
}
if (this.comboboxEl && avoidComboFocus)
this.comboboxEl.setAttribute("data-focus", "false");
let canClick = true;
// check if can click based on next dropdown state
if (disableClose && !dropdownEl.classList.contains("hidden"))
canClick = false;
if (disableOpen && dropdownEl.classList.contains("hidden"))
canClick = false;
if (canClick) dropdownBtn.click();
// Case avoid focus on combobox, we need to reset here because the focusin event is not triggered
if (this.comboboxEl && avoidComboFocus) this.runComboFilter();
// Reset to default state
if (this.comboboxEl) this.comboboxEl.setAttribute("data-focus", "true");
}
resetFilter() {

View file

@ -286,6 +286,14 @@
@apply transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-300;
}
.core-card-text-doc {
@apply transition duration-300 ease-in-out mb-0 font-sans text-sm leading-normal dark:text-gray-300 px-1 mt-4 mb-2;
}
.core-card-text-doc-link {
@apply hover:brightness-90 cursor-pointer mt-2 underline text-sky-500;
}
.center.core-card-text {
@apply text-center;
}
@ -343,7 +351,15 @@
}
.core-card-list {
@apply ml-2 mr-2 mb-2 mt-2 h-fit content-start md:col-span-6 overflow-x-hidden overflow-y-auto grid grid-cols-12 max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words dark:brightness-110 bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border;
@apply ml-2 mr-2 mb-2 mt-2 content-start md:col-span-6 overflow-x-hidden overflow-y-auto grid grid-cols-12 max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words dark:brightness-110 bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border;
}
.core-card-list.no-data {
@apply place-content-stretch;
}
.core-card-list-no-data {
@apply pb-[2rem] text-2xl text-center mb-0;
}
.w-small.core-card-list {
@ -522,6 +538,10 @@
@apply text-sm tracking-wide dark:brightness-90 inline-block px-6 py-3 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-yellow-500 hover:bg-yellow-500/80 focus:bg-yellow-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;
}
.core-card-upload-btn {
@apply w-fit disabled:hover:translate-y-0 disabled:cursor-not-allowed 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 tracking-wide dark:brightness-125 hover:brightness-75 inline-block px-6 py-3 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-gradient-to-tl bg-primary leading-normal text-xs ease-in shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md;
}
.core-card-test-status-container {
@apply mx-1 flex justify-center items-center;
}

View file

@ -70,6 +70,7 @@ module.exports = {
"text-yellow-500",
"text-green-500",
"text-red-500",
"text-sky-500",
],
presets: [],

View file

@ -103,7 +103,9 @@
<p class="my-2 mr-2 dark:text-gray-300 text-center font-bold">{{ pro_services }} services allowed</p>
{% endif %}
{% if pro_expire %}
<p class="my-2 mr-2 dark:text-gray-300 text-center">License expiration date : <span data-expire class="font-bold">{{ pro_expire }}</span></p>
<p class="my-2 mr-2 dark:text-gray-300 text-center">
License expiration date : <span data-expire class="font-bold">{{ pro_expire }}</span>
</p>
{% endif %}
</div>
{% endif %}
@ -115,60 +117,66 @@
</div>
{% endif %}
</div>
<form class="mt-6 relative col-span-12 grid grid-cols-12 w-full justify-items-center"
id="activate-key-form"
action="account"
method="POST"
autocomplete="off">
<div class="col-span-12">
<h5 class="text-xl my-1 transition duration-300 ease-in-out text-md font-bold m-0 dark:text-gray-200">{%if is_pro_version %}UPDATE KEY {% else %} ACTIVATE KEY {% endif %}</h5>
<form class="mt-6 relative col-span-12 grid grid-cols-12 w-full justify-items-center"
id="activate-key-form"
action="account"
method="POST"
autocomplete="off">
<div class="col-span-12">
<h5 class="text-xl my-1 transition duration-300 ease-in-out text-md font-bold m-0 dark:text-gray-200">
{% if is_pro_version %}
UPDATE KEY
{% else %}
ACTIVATE KEY
{% endif %}
</h5>
</div>
<input type="hidden" name="operation" value="activate-key" />
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input type="hidden"
name="next"
value="{{ request.values.get('next', '') }}" />
<div data-input-group
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"
id="license"
name="license"
class="col-span-12 regular-input"
placeholder="enter new license key"
value=""
pattern="^.*$"
required />
<div data-setting-password-container
class="absolute flex right-6 md:right-8 h-5 w-5 top-[55%] md:top-[53%]">
<button data-setting-password="visible"
class="h-5 w-5 flex items-center align-middle dark:fill-blue-500 hover:brightness-75 transition-all"
type="button">
<svg class="fill-primary pointer-events-none dark:fill-blue-500 hover:brightness-75 transition-all"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 576 512">
<path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z" />
</svg>
</button>
<button data-setting-password="invisible"
class="hidden -translate-y-0.2 scale-110 h-5 w-5 items-center align-middle"
type="button">
<svg class="fill-primary pointer-events-none dark:fill-blue-500 hover:brightness-75 transition-all"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512">
<path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z" />
</svg>
</button>
</div>
<input type="hidden" name="operation" value="activate-key" />
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input type="hidden"
name="next"
value="{{ request.values.get('next', '') }}" />
<div data-input-group
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"
id="license"
name="license"
class="col-span-12 regular-input"
placeholder="enter new license key"
value=""
pattern="^.*$"
required />
<div data-setting-password-container
class="absolute flex right-6 md:right-8 h-5 w-5 top-[55%] md:top-[53%]">
<button data-setting-password="visible"
class="h-5 w-5 flex items-center align-middle dark:fill-blue-500 hover:brightness-75 transition-all"
type="button">
<svg class="fill-primary pointer-events-none dark:fill-blue-500 hover:brightness-75 transition-all"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 576 512">
<path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z" />
</svg>
</button>
<button data-setting-password="invisible"
class="hidden -translate-y-0.2 scale-110 h-5 w-5 items-center align-middle"
type="button">
<svg class="fill-primary pointer-events-none dark:fill-blue-500 hover:brightness-75 transition-all"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 640 512">
<path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z" />
</svg>
</button>
</div>
</div>
<div class="col-span-12 flex justify-center mt-6">
<button type="submit"
id="activate-key-button"
name="activate-key-button"
class="valid-btn">SAVE</button>
</div>
</form>
</div>
<div class="col-span-12 flex justify-center mt-6">
<button type="submit"
id="activate-key-button"
name="activate-key-button"
class="valid-btn">SAVE</button>
</div>
</form>
</div>
<div data-tab-item="username"
class="hidden grid grid-cols-12 w-full justify-items-center">

View file

@ -48,19 +48,12 @@
<div class="{% if bans|length == 0 %}hidden{% endif %} h-fit col-span-12 md:col-span-4 3xl:col-span-3 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">
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
<div role="grid" class="card-detail-container">
{% for info in bans_info %}
<div role="row"
class="card-detail-item">
<p role="gridcell"
class="card-detail-item-title">
{{ info['name'] }}
</p>
<p role="gridcell"
class="card-detail-item-subtitle">
{{ info['data'] }}
</p>
</div>
{% endfor %}
{% for info in bans_info %}
<div role="row" class="card-detail-item">
<p role="gridcell" class="card-detail-item-title">{{ info['name'] }}</p>
<p role="gridcell" class="card-detail-item-subtitle">{{ info['data'] }}</p>
</div>
{% endfor %}
</div>
</div>
<!-- end info -->

View file

@ -125,7 +125,9 @@
<input data-ban-add-inp type="hidden" name="data" value="" />
<!-- action button -->
<div class="w-full justify-center flex mt-6 mb-4">
<button data-bans-modal-close type="button" class="dark:bg-slate-800 close-btn mr-3 text-base">Close</button>
<button data-bans-modal-close
type="button"
class="dark:bg-slate-800 close-btn mr-3 text-base">Close</button>
<button disabled data-bans-modal-submit type="submit" class="valid-btn">Add</button>
</div>
<!-- end action button-->

View file

@ -14,21 +14,16 @@
] %}
<div class="h-fit col-span-12 md:col-span-4 3xl:col-span-3 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">
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
<div role="grid" class="card-detail-container">
{% for info in configs_info %}
<div role="row"
class="card-detail-item">
<p role="gridcell"
class="card-detail-item-title">
{{ info['name'] }}
</p>
<p data-info-{{ info['id'] }} role="gridcell"
class="card-detail-item-subtitle">
{{ info['data'] }}
</p>
<div role="grid" class="card-detail-container">
{% for info in configs_info %}
<div role="row" class="card-detail-item">
<p role="gridcell" class="card-detail-item-title">{{ info['name'] }}</p>
<p data-info-{{ info['id'] }} role="gridcell" class="card-detail-item-subtitle">
{{ info['data'] }}
</p>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
</div>
<!-- end info -->
<!-- filter -->

View file

@ -92,7 +92,6 @@
role="listbox"
data-global-config-setting-select-dropdown="{{ filter['id'] }}"
class="mt-1 hidden z-100 absolute flex-col w-full translate-y-16 max-h-[350px] overflow-hidden overflow-y-auto">
{% for value in filter['values'] %}
<button role="option"
data-global-config-setting-select-dropdown-btn="{{ filter['id'] }}"

View file

@ -10,42 +10,42 @@
<title>BunkerWeb UI</title>
<link rel="icon" type="image/x-icon" href="images/favicon.ico" />
<!-- tailwind style -->
<link rel="stylesheet" type="text/css" href="./css/plugins.css" />
<link rel="stylesheet" type="text/css" href="./css/dashboard.css" />
<script type="module" src="./js/global.js" nonce="{{ script_nonce }}"></script>
<script src="./js/plugins/utils.js" nonce="{{ script_nonce }}"></script>
<link rel="stylesheet" href="css/plugins.css" />
<link rel="stylesheet" href="css/dashboard.css" />
<script type="module" src="js/global.js" nonce="{{ script_nonce }}"></script>
<script src="js/plugins/utils.js" nonce="{{ script_nonce }}"></script>
<script async
src="./js/utils/purify/purify.min.js"
src="js/utils/purify/purify.min.js"
nonce="{{ script_nonce }}"></script>
<script src="./js/editor/ace.js" nonce="{{ script_nonce }}"></script>
<script src="js/editor/ace.js" nonce="{{ script_nonce }}"></script>
{% if current_endpoint == "global_config" %}
<script type="module" src="./js/global_config.js" nonce="{{ script_nonce }}"></script>
<script type="module" src="js/global_config.js" nonce="{{ script_nonce }}"></script>
{% elif current_endpoint == "configs" %}
<script type="module" src="./js/configs.js" nonce="{{ script_nonce }}"></script>
<script type="module" src="js/configs.js" nonce="{{ script_nonce }}"></script>
{% elif current_endpoint == "services" %}
<script type="module" src="./js/services.js" nonce="{{ script_nonce }}"></script>
<script type="module" src="js/services.js" nonce="{{ script_nonce }}"></script>
{% elif current_endpoint == "plugins" %}
<script type="module" src="./js/plugins.js" nonce="{{ script_nonce }}"></script>
<script type="module" src="js/plugins.js" nonce="{{ script_nonce }}"></script>
{% elif current_endpoint == "cache" %}
<script type="module" src="./js/cache.js" nonce="{{ script_nonce }}"></script>
<script type="module" src="js/cache.js" nonce="{{ script_nonce }}"></script>
{% elif current_endpoint == "logs" %}
<link rel="stylesheet" type="text/css" href="./css/flatpickr.css" />
<link rel="stylesheet" type="text/css" href="./css/flatpickr.dark.css" />
<script defer src="./js/utils/flatpickr.js" nonce="{{ script_nonce }}"></script>
<script type="module" src="./js/logs.js" nonce="{{ script_nonce }}"></script>
<link rel="stylesheet" href="css/flatpickr.css" />
<link rel="stylesheet" href="css/flatpickr.dark.css" />
<script defer src="js/utils/flatpickr.js" nonce="{{ script_nonce }}"></script>
<script type="module" src="js/logs.js" nonce="{{ script_nonce }}"></script>
<link rel="stylesheet"
type="text/css"
href="./css/datepicker-foundation.css" />
href="css/datepicker-foundation.css" />
{% elif current_endpoint == "jobs" %}
<script type="module" src="./js/jobs.js" nonce="{{ script_nonce }}"></script>
<script type="module" src="js/jobs.js" nonce="{{ script_nonce }}"></script>
{% elif current_endpoint == "account" %}
<script type="module" src="./js/account.js" nonce="{{ script_nonce }}"></script>
<script type="module" src="js/account.js" nonce="{{ script_nonce }}"></script>
{% elif current_endpoint == "reports" %}
<script type="module" src="./js/reports.js" nonce="{{ script_nonce }}"></script>
<script type="module" src="js/reports.js" nonce="{{ script_nonce }}"></script>
{% elif current_endpoint == "bans" %}
<link rel="stylesheet" type="text/css" href="./css/flatpickr.css" />
<link rel="stylesheet" type="text/css" href="./css/flatpickr.dark.css" />
<script defer src="./js/utils/flatpickr.js" nonce="{{ script_nonce }}"></script>
<script type="module" src="./js/bans.js" nonce="{{ script_nonce }}"></script>
<link rel="stylesheet" href="css/flatpickr.css" />
<link rel="stylesheet" href="css/flatpickr.dark.css" />
<script defer src="js/utils/flatpickr.js" nonce="{{ script_nonce }}"></script>
<script type="module" src="js/bans.js" nonce="{{ script_nonce }}"></script>
{% endif %}
</head>

View file

@ -27,16 +27,9 @@
{% set instance_details = [{"name" : "TYPE", "value" : instance['_type']},{"name" : "HOSTNAME", "value" : instance['hostname']}] %}
<!-- detail -->
{% for detail in instance_details %}
<div role="row"
class="card-detail-item">
<p role="gridcell"
class="card-detail-item-title">
{{ detail['name'] }}
</p>
<p role="gridcell"
class="card-detail-item-subtitle">
{{ detail['value'] }}
</p>
<div role="row" class="card-detail-item">
<p role="gridcell" class="card-detail-item-title">{{ detail['name'] }}</p>
<p role="gridcell" class="card-detail-item-subtitle">{{ detail['value'] }}</p>
</div>
{% endfor %}
<!-- end detail -->

View file

@ -14,19 +14,12 @@
<div class="h-fit col-span-12 md:col-span-4 3xl:col-span-3 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">
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
<div role="grid" class="card-detail-container">
{% for info in jobs_info %}
<div role="row"
class="card-detail-item">
<p role="gridcell"
class="card-detail-item-title">
{{ info['name'] }}
</p>
<p role="gridcell"
class="card-detail-item-subtitle">
{{ info['data'] }}
</p>
</div>
{% endfor %}
{% for info in jobs_info %}
<div role="row" class="card-detail-item">
<p role="gridcell" class="card-detail-item-title">{{ info['name'] }}</p>
<p role="gridcell" class="card-detail-item-subtitle">{{ info['data'] }}</p>
</div>
{% endfor %}
</div>
</div>
<!-- end info -->
@ -150,138 +143,143 @@
<h5 class="font-bold dark:text-white/90 mx-2 text-white">No jobs match</h5>
</div>
</div>
<div data-jobs-list-container
class="w-full overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
<div class="overflow-auto w-full col-span-12 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
<div class="col-span-12">
<h5 class="mb-4 mt-2 font-bold dark:text-white/90 mx-2">JOBS LIST</h5>
</div>
<div class="col-span-12 overflow-y-auto overflow-x-auto">
<!-- list container-->
{% set job_headers = [
{
"name": "Name",
"custom_class": "col-span-3"
},
{
"name": "Last run",
"custom_class": "col-span-3"
},
{
"name": "Every",
"custom_class": "col-span-1"
},
{
"name": "Reload",
"custom_class": "flex justify-center col-span-1"
},
{
"name": "Success",
"custom_class": "flex justify-center col-span-1"
},
{
"name": "Files",
"custom_class": "col-span-3"
}
] %}
<div class="min-w-[900px] w-full grid grid-cols-12 rounded p-2">
<!-- header-->
{% for header in job_headers %}
<p class="{{ header['custom_class'] }} dark:text-gray-100 h-8 text-sm font-bold m-0 pb-2 border-b border-gray-400">
{{ header['name'] }}
</p>
{% endfor %}
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full" data-jobs-list>
{% for job_name, value in jobs.items() %}
<!-- job item-->
{% set jobs_data = [
{"type" : "text", "filter_name" : "name", "value" : job_name, "custom_class" : "col-span-3"},
{"type" : "text", "filter_name" : "last_run", "value" : value['last_run'], "custom_class" : "col-span-3"},
{"type" : "text", "filter_name" : "every", "value" : value['every'], "custom_class" : "col-span-1"},
{"type" : "check", "filter_name" : "reload", "value" : value['reload'], "custom_class" : "col-span-1"},
{"type" : "check", "filter_name" : "success", "value" : value['success'], "custom_class" : "col-span-1"},
{"type" : "select", "filter_name" : "success", "value" : value['success'], "custom_class" : "col-span-3"},
] %}
<li data-jobs-item
class="items-center grid grid-cols-12 border-b border-gray-300 py-2.5">
{% for data in jobs_data %}
{% if data['type'] == "text" %}
<p class="{{ data['custom_class'] }} dark:text-gray-400 text-sm m-0 my-1" data-jobs-{{ data['filter_name'] }}="{{ data['value'] }}">
{{ data['value'] }}
</p>
{% endif %}
{% if data['type'] == "check" and data['value'] %}
<p class="{{ data['custom_class'] }} flex justify-center dark:text-gray-400 text-sm m-0 my-1" data-jobs-{{ data['filter_name'] }}="true">
<svg class="fill-green-500 h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512">
<path d="M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z" />
</svg>
</p>
{% endif %}
{% if data['type'] == "check" and not data['value'] %}
<p class="{{ data['custom_class'] }} flex justify-center dark:text-gray-400 text-sm m-0 my-1" data-jobs-{{ data['filter_name'] }}="false">
<svg class="fill-red-500 h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512">
<path d="M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM175 175c9.4-9.4 24.6-9.4 33.9 0l47 47 47-47c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-47 47 47 47c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-47-47-47 47c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l47-47-47-47c-9.4-9.4-9.4-24.6 0-33.9z" />
</svg>
</p>
{% endif %}
{% if data['type'] == "select" %}
<div class="{{ data['custom_class'] }} relative dark:text-gray-400 text-sm m-0 my-1"
data-jobs-files>
{% if value['cache'] %}
<button data-jobs-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 }}"
data-jobs-setting-select-text="{{ job_name }}">files</span>
<!-- chevron -->
<svg data-jobs-setting-select="{{ job_name }}"
class="transition-transform h-4 w-4 fill-gray-500"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512">
<path d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z" />
</svg>
</button>
<!-- end chevron -->
<!-- dropdown-->
<div data-jobs-setting-select-dropdown="{{ job_name }}"
class="hidden z-100 absolute h-full flex-col w-full translate-y-0.5">
{% for file in value['cache'] %}
<button data-jobs-download="{{ job_name }}"
data-jobs-file="{{ file['file_name'] }}"
data-jobs-setting-select-dropdown-btn="{{ job_name }}"
value="files"
class="{% if loop.index == loop.length %}rounded-b-lg {% endif %}{% if loop.first %}rounded-t-lg{% endif %} border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 my-0 relative py-2 px-3 text-left align-middle transition-all rounded-none cursor-pointer leading-normal text-sm ease-in tracking-tight-rem dark:border-slate-600 dark:text-gray-300 bg-white dark:bg-slate-700 text-gray-700">
<span class="flex justify-start items-center">
<svg class="h-5.5 w-5.5 stroke-sky-500"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75l3 3m0 0l3-3m-3 3v-7.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="transition duration-300 ease-in-out text-gray-700 dark:text-gray-300 ml-2">{{ file['file_name'] }}</span>
</span>
</button>
{% endfor %}
</div>
<!-- end dropdown-->
{% endif %}
</div>
{% endif %}
{% endfor %}
</li>
<!-- end job item-->
{% endfor %}
</ul>
<!-- end list-->
</div>
<!-- end list container-->
<h5 class="mx-4 mt-2 font-bold dark:text-white/90 mx-2">JOBS LIST</h5>
</div>
<div data-jobs-list-container
class="relative min-w-[900px] w-full overflow-auto grid grid-cols-12 max-h-100 sm:max-h-125">
<div class="col-span-12">
<!-- list container-->
{% set job_headers = [
{
"name": "Name",
"custom_class": "col-span-3"
},
{
"name": "Last run",
"custom_class": "col-span-3"
},
{
"name": "Every",
"custom_class": "col-span-1"
},
{
"name": "Reload",
"custom_class": "flex justify-center col-span-1"
},
{
"name": "Success",
"custom_class": "flex justify-center col-span-1"
},
{
"name": "Files",
"custom_class": "col-span-3"
}
] %}
<div class="w-full grid grid-cols-12 rounded p-2">
<!-- header-->
{% for header in job_headers %}
<p class="{{ header['custom_class'] }} dark:text-gray-100 h-8 text-sm font-bold m-0 pb-2 border-b border-gray-400">
{{ header['name'] }}
</p>
{% endfor %}
<!-- end header-->
<!-- list -->
<ul class="col-span-12 w-full" data-jobs-list>
{% for job_name, value in jobs.items() %}
<!-- job item-->
{% set jobs_data = [
{"type" : "text", "filter_name" : "name", "value" : job_name, "custom_class" : "col-span-3"},
{"type" : "text", "filter_name" : "last_run", "value" : value['last_run'], "custom_class" : "col-span-3"},
{"type" : "text", "filter_name" : "every", "value" : value['every'], "custom_class" : "col-span-1"},
{"type" : "check", "filter_name" : "reload", "value" : value['reload'], "custom_class" : "col-span-1"},
{"type" : "check", "filter_name" : "success", "value" : value['success'], "custom_class" : "col-span-1"},
{"type" : "select", "filter_name" : "success", "value" : value['success'], "custom_class" : "col-span-3"},
] %}
<li data-jobs-item
class="items-center grid grid-cols-12 border-b border-gray-300 py-2.5">
{% for data in jobs_data %}
{% if data['type'] == "text" %}
<p class="{{ data['custom_class'] }} dark:text-gray-400 text-sm m-0 my-1" data-jobs-{{ data['filter_name'] }}="{{ data['value'] }}">
{{ data['value'] }}
</p>
{% endif %}
{% if data['type'] == "check" and data['value'] %}
<p class="{{ data['custom_class'] }} flex justify-center dark:text-gray-400 text-sm m-0 my-1" data-jobs-{{ data['filter_name'] }}="true">
<svg class="fill-green-500 h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512">
<path d="M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM369 209L241 337c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L335 175c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z" />
</svg>
</p>
{% endif %}
{% if data['type'] == "check" and not data['value'] %}
<p class="{{ data['custom_class'] }} flex justify-center dark:text-gray-400 text-sm m-0 my-1" data-jobs-{{ data['filter_name'] }}="false">
<svg class="fill-red-500 h-5 w-5"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512">
<path d="M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM175 175c9.4-9.4 24.6-9.4 33.9 0l47 47 47-47c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9l-47 47 47 47c9.4 9.4 9.4 24.6 0 33.9s-24.6 9.4-33.9 0l-47-47-47 47c-9.4 9.4-24.6 9.4-33.9 0s-9.4-24.6 0-33.9l47-47-47-47c-9.4-9.4-9.4-24.6 0-33.9z" />
</svg>
</p>
{% endif %}
{% if data['type'] == "select" %}
<div class="{{ data['custom_class'] }} relative dark:text-gray-400 text-sm m-0 my-1"
data-jobs-files>
{% if value['cache'] %}
<button data-jobs-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 }}"
data-jobs-setting-select-text="{{ job_name }}">files</span>
<!-- chevron -->
<svg data-jobs-setting-select="{{ job_name }}"
class="transition-transform h-4 w-4 fill-gray-500"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512">
<path d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z" />
</svg>
</button>
<!-- end chevron -->
<!-- dropdown-->
<div data-jobs-setting-select-dropdown="{{ job_name }}"
class="hidden z-100 absolute h-full flex-col w-full translate-y-0.5">
{% for file in value['cache'] %}
<button data-jobs-plugin="{{ value['plugin_id'] }}"
data-jobs-download="{{ job_name }}"
data-jobs-file="{{ file['file_name'] }}"
data-jobs-setting-select-dropdown-btn="{{ job_name }}"
value="files"
class="{% if loop.index == loop.length %}rounded-b-lg {% endif %}{% if loop.first %}rounded-t-lg{% endif %} border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 my-0 relative py-2 px-3 text-left align-middle transition-all rounded-none cursor-pointer leading-normal text-sm ease-in tracking-tight-rem dark:border-slate-600 dark:text-gray-300 bg-white dark:bg-slate-700 text-gray-700">
<span class="flex justify-start items-center">
<svg class="h-5.5 w-5.5 stroke-sky-500"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75l3 3m0 0l3-3m-3 3v-7.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="transition duration-300 ease-in-out text-gray-700 dark:text-gray-300 ml-2">{{ file['file_name'] }}</span>
</span>
</button>
{% endfor %}
</div>
<!-- end dropdown-->
{% endif %}
</div>
{% endif %}
{% endfor %}
</li>
<!-- end job item-->
{% endfor %}
</ul>
<!-- end list-->
</div>
<!-- end list container-->
</div>
</div>
</div>
{% endblock content %}

View file

@ -35,14 +35,14 @@
<!-- logo and version -->
<div class="w-full">
<a aria-label="link to home"
class="flex justify-center px-8 m-0 text-sm whitespace-nowrap dark:text-white text-slate-700"
href="{% if current_endpoint == 'home' %}#{% else %}loading?next={{ url_for("home") }}{% endif %}">
class="flex justify-center px-8 m-0 text-sm whitespace-nowrap dark:text-white text-slate-700"
href="{% if current_endpoint == 'home' %}#{% else %}loading?next={{ url_for("home") }}{% endif %}">
<img src="images/logo-menu-2.png"
class="hidden dark:inline w-28 sm:w-36 transition-all duration-200 h-8 sm:h-10"
alt="main logo" />
class="hidden dark:inline w-28 sm:w-36 transition-all duration-200 h-8 sm:h-10"
alt="main logo" />
<img src="images/logo-menu.png"
class="dark:hidden inline w-28 sm:w-36 transition-all duration-200 h-8 sm:h-10"
alt="main logo" />
class="dark:hidden inline w-28 sm:w-36 transition-all duration-200 h-8 sm:h-10"
alt="main logo" />
</a>
</div>
<div class="mt-2 w-full px-1">
@ -50,197 +50,194 @@
{{ username }}
</h1>
<a class="block underline mb-2 text-gray-600 dark:text-gray-400 text-sm text-center hover:brightness-90"
href="{% if current_endpoint == 'account' %}#{% else %}loading?next={{ url_for("account") }}{% endif %}">manage account
href="{% if current_endpoint == 'account' %}#{% else %}loading?next={{ url_for("account") }}{% endif %}">manage account
</a>
</div>
<hr class="h-px mt-0 bg-transparent bg-gradient-to-r from-transparent via-black/40 to-transparent dark:bg-gradient-to-r dark:from-transparent dark:via-white dark:to-transparent" />
<!-- end logo version -->
</div>
<!-- list items -->
<div class="items-center block w-auto overflow-auto grow basis-full">
<!-- default anchor -->
<ul class="flex flex-col pl-0 mb-0">
{% set paths = [
"home",
"instances",
"global_config",
"services",
"configs",
"plugins",
"cache",
"reports",
"bans",
"jobs",
"logs"
] %}
<!-- loop paths -->
{% for path in paths %}
<li class="mt-0.5 w-full">
<a class="{% if current_endpoint == path %} font-semibold text-slate-700 dark:bg-primary/50 rounded-lg dark:hover:bg-primary/60 bg-primary/10 hover:bg-primary/30 {% else %} dark:hover:bg-primary/20 hover:bg-primary/5 {% endif %} dark:text-gray-200 py-1 ease-nav-brand my-0 mx-2 flex items-center whitespace-nowrap rounded-lg px-4 transition text-sm"
href="{% if current_endpoint == path %}#{% else %}loading?next={{ url_for(path) }}{% endif %}">
<div class="mr-2 flex items-center justify-center rounded-lg bg-center stroke-0 text-center p-1 xl:p-1.5">
{% if path == "home" %}
<svg class="stroke-sky-500 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
</svg>
{% endif %}
{% if path == "instances" %}
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="stroke-stone-500 h-5.5 w-5.5 relative">
<path stroke-linecap="round" stroke-linejoin="round" d="m21 7.5-9-5.25L3 7.5m18 0-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9" />
</svg>
{% endif %}
{% if path == "global_config" %}
<svg class="stroke-blue-400 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75" />
</svg>
{% endif %}
{% if path == "services" %}
<svg class="stroke-orange-500 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M21.75 17.25v-.228a4.5 4.5 0 00-.12-1.03l-2.268-9.64a3.375 3.375 0 00-3.285-2.602H7.923a3.375 3.375 0 00-3.285 2.602l-2.268 9.64a4.5 4.5 0 00-.12 1.03v.228m19.5 0a3 3 0 01-3 3H5.25a3 3 0 01-3-3m19.5 0a3 3 0 00-3-3H5.25a3 3 0 00-3 3m16.5 0h.008v.008h-.008v-.008zm-3 0h.008v.008h-.008v-.008z" />
</svg>
{% endif %}
{% if path == "configs" %}
<svg class="stroke-blue-500 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
{% endif %}
{% if path == "plugins" %}
<svg class="stroke-yellow-400 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M14.25 6.087c0-.355.186-.676.401-.959.221-.29.349-.634.349-1.003 0-1.036-1.007-1.875-2.25-1.875s-2.25.84-2.25 1.875c0 .369.128.713.349 1.003.215.283.401.604.401.959v0a.64.64 0 01-.657.643 48.39 48.39 0 01-4.163-.3c.186 1.613.293 3.25.315 4.907a.656.656 0 01-.658.663v0c-.355 0-.676-.186-.959-.401a1.647 1.647 0 00-1.003-.349c-1.036 0-1.875 1.007-1.875 2.25s.84 2.25 1.875 2.25c.369 0 .713-.128 1.003-.349.283-.215.604-.401.959-.401v0c.31 0 .555.26.532.57a48.039 48.039 0 01-.642 5.056c1.518.19 3.058.309 4.616.354a.64.64 0 00.657-.643v0c0-.355-.186-.676-.401-.959a1.647 1.647 0 01-.349-1.003c0-1.035 1.008-1.875 2.25-1.875 1.243 0 2.25.84 2.25 1.875 0 .369-.128.713-.349 1.003-.215.283-.4.604-.4.959v0c0 .333.277.599.61.58a48.1 48.1 0 005.427-.63 48.05 48.05 0 00.582-4.717.532.532 0 00-.533-.57v0c-.355 0-.676.186-.959.401-.29.221-.634.349-1.003.349-1.035 0-1.875-1.007-1.875-2.25s.84-2.25 1.875-2.25c.37 0 .713.128 1.003.349.283.215.604.401.96.401v0a.656.656 0 00.658-.663 48.422 48.422 0 00-.37-5.36c-1.886.342-3.81.574-5.766.689a.578.578 0 01-.61-.58v0z" />
</svg>
{% endif %}
{% if path == "cache" %}
<svg class="stroke-pink-600 h-5.5 w-5.5 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M20.25 7.5l-.625 10.632a2.25 2.25 0 01-2.247 2.118H6.622a2.25 2.25 0 01-2.247-2.118L3.75 7.5M10 11.25h4M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125z" />
</svg>
{% endif %}
{% if path == "reports" %}
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="stroke-amber-500 dark:stroke-amber-500 h-6 w-6 relative">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 3v1.5M3 21v-6m0 0 2.77-.693a9 9 0 0 1 6.208.682l.108.054a9 9 0 0 0 6.086.71l3.114-.732a48.524 48.524 0 0 1-.005-10.499l-3.11.732a9 9 0 0 1-6.085-.711l-.108-.054a9 9 0 0 0-6.208-.682L3 4.5M3 15V4.5" />
</svg>
{% endif %}
{% if path == "bans" %}
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="stroke-red-500 dark:stroke-red-500 h-6 w-6 relative">
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636" />
</svg>
{% endif %}
{% if path == "jobs" %}
<svg class="stroke-emerald-600 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.125 2.25h-4.5c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125v-9M10.125 2.25h.375a9 9 0 019 9v.375M10.125 2.25A3.375 3.375 0 0113.5 5.625v1.5c0 .621.504 1.125 1.125 1.125h1.5a3.375 3.375 0 013.375 3.375M9 15l2.25 2.25L15 12" />
</svg>
{% endif %}
{% if path == "logs" %}
<svg class="stroke-gray-600 dark:fill-gray-500 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 6.75h12M8.25 12h12m-12 5.25h12M3.75 6.75h.007v.008H3.75V6.75zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zM3.75 12h.007v.008H3.75V12zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm-.375 5.25h.007v.008H3.75v-.008zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" />
</svg>
{% endif %}
</div>
<span class="ml-1 duration-300 dark:text-gray-200 pointer-events-none ease">{{ path[0]|upper }}{{ path[1:].replace('_', ' ') }}</span>
</a>
</li>
{% endfor %}
<!-- end loop paths-->
</ul>
<!-- end default anchor -->
<ul>
<li class="w-full mt-4">
<h6 class="pl-6 ml-2 text-xs font-bold leading-tight uppercase dark:text-gray-400 dark:opacity-100 opacity-60">
PLUGINS PAGE
</h6>
<!-- list items -->
<div class="items-center block w-auto overflow-auto grow basis-full">
<!-- default anchor -->
<ul class="flex flex-col pl-0 mb-0">
{% set paths = [
"home",
"instances",
"global_config",
"services",
"configs",
"plugins",
"cache",
"reports",
"bans",
"jobs",
"logs"
] %}
<!-- loop paths -->
{% for path in paths %}
<li class="mt-0.5 w-full">
<a class="{% if current_endpoint == path %} font-semibold text-slate-700 dark:bg-primary/50 rounded-lg dark:hover:bg-primary/60 bg-primary/10 hover:bg-primary/30 {% else %} dark:hover:bg-primary/20 hover:bg-primary/5 {% endif %} dark:text-gray-200 py-1 ease-nav-brand my-0 mx-2 flex items-center whitespace-nowrap rounded-lg px-4 transition text-sm"
href="{% if current_endpoint == path %}#{% else %}loading?next={{ url_for(path) }}{% endif %}">
<div class="mr-2 flex items-center justify-center rounded-lg bg-center stroke-0 text-center p-1 xl:p-1.5">
{% if path == "home" %}
<svg class="stroke-sky-500 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
</svg>
{% endif %}
{% if path == "instances" %}
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="stroke-stone-500 h-5.5 w-5.5 relative">
<path stroke-linecap="round" stroke-linejoin="round" d="m21 7.5-9-5.25L3 7.5m18 0-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9" />
</svg>
{% endif %}
{% if path == "global_config" %}
<svg class="stroke-blue-400 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75" />
</svg>
{% endif %}
{% if path == "services" %}
<svg class="stroke-orange-500 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M21.75 17.25v-.228a4.5 4.5 0 00-.12-1.03l-2.268-9.64a3.375 3.375 0 00-3.285-2.602H7.923a3.375 3.375 0 00-3.285 2.602l-2.268 9.64a4.5 4.5 0 00-.12 1.03v.228m19.5 0a3 3 0 01-3 3H5.25a3 3 0 01-3-3m19.5 0a3 3 0 00-3-3H5.25a3 3 0 00-3 3m16.5 0h.008v.008h-.008v-.008zm-3 0h.008v.008h-.008v-.008z" />
</svg>
{% endif %}
{% if path == "configs" %}
<svg class="stroke-blue-500 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
{% endif %}
{% if path == "plugins" %}
<svg class="stroke-yellow-400 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M14.25 6.087c0-.355.186-.676.401-.959.221-.29.349-.634.349-1.003 0-1.036-1.007-1.875-2.25-1.875s-2.25.84-2.25 1.875c0 .369.128.713.349 1.003.215.283.401.604.401.959v0a.64.64 0 01-.657.643 48.39 48.39 0 01-4.163-.3c.186 1.613.293 3.25.315 4.907a.656.656 0 01-.658.663v0c-.355 0-.676-.186-.959-.401a1.647 1.647 0 00-1.003-.349c-1.036 0-1.875 1.007-1.875 2.25s.84 2.25 1.875 2.25c.369 0 .713-.128 1.003-.349.283-.215.604-.401.959-.401v0c.31 0 .555.26.532.57a48.039 48.039 0 01-.642 5.056c1.518.19 3.058.309 4.616.354a.64.64 0 00.657-.643v0c0-.355-.186-.676-.401-.959a1.647 1.647 0 01-.349-1.003c0-1.035 1.008-1.875 2.25-1.875 1.243 0 2.25.84 2.25 1.875 0 .369-.128.713-.349 1.003-.215.283-.4.604-.4.959v0c0 .333.277.599.61.58a48.1 48.1 0 005.427-.63 48.05 48.05 0 00.582-4.717.532.532 0 00-.533-.57v0c-.355 0-.676.186-.959.401-.29.221-.634.349-1.003.349-1.035 0-1.875-1.007-1.875-2.25s.84-2.25 1.875-2.25c.37 0 .713.128 1.003.349.283.215.604.401.96.401v0a.656.656 0 00.658-.663 48.422 48.422 0 00-.37-5.36c-1.886.342-3.81.574-5.766.689a.578.578 0 01-.61-.58v0z" />
</svg>
{% endif %}
{% if path == "cache" %}
<svg class="stroke-pink-600 h-5.5 w-5.5 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M20.25 7.5l-.625 10.632a2.25 2.25 0 01-2.247 2.118H6.622a2.25 2.25 0 01-2.247-2.118L3.75 7.5M10 11.25h4M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125z" />
</svg>
{% endif %}
{% if path == "reports" %}
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="stroke-amber-500 dark:stroke-amber-500 h-6 w-6 relative">
<path stroke-linecap="round" stroke-linejoin="round" d="M3 3v1.5M3 21v-6m0 0 2.77-.693a9 9 0 0 1 6.208.682l.108.054a9 9 0 0 0 6.086.71l3.114-.732a48.524 48.524 0 0 1-.005-10.499l-3.11.732a9 9 0 0 1-6.085-.711l-.108-.054a9 9 0 0 0-6.208-.682L3 4.5M3 15V4.5" />
</svg>
{% endif %}
{% if path == "bans" %}
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="stroke-red-500 dark:stroke-red-500 h-6 w-6 relative">
<path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 0 0 5.636 5.636m12.728 12.728A9 9 0 0 1 5.636 5.636m12.728 12.728L5.636 5.636" />
</svg>
{% endif %}
{% if path == "jobs" %}
<svg class="stroke-emerald-600 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M10.125 2.25h-4.5c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125v-9M10.125 2.25h.375a9 9 0 019 9v.375M10.125 2.25A3.375 3.375 0 0113.5 5.625v1.5c0 .621.504 1.125 1.125 1.125h1.5a3.375 3.375 0 013.375 3.375M9 15l2.25 2.25L15 12" />
</svg>
{% endif %}
{% if path == "logs" %}
<svg class="stroke-gray-600 dark:fill-gray-500 h-6 w-6 relative"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 6.75h12M8.25 12h12m-12 5.25h12M3.75 6.75h.007v.008H3.75V6.75zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zM3.75 12h.007v.008H3.75V12zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0zm-.375 5.25h.007v.008H3.75v-.008zm.375 0a.375.375 0 11-.75 0 .375.375 0 01.75 0z" />
</svg>
{% endif %}
</div>
<span class="ml-1 duration-300 dark:text-gray-200 pointer-events-none ease">{{ path[0]|upper }}{{ path[1:].replace('_', ' ') }}</span>
</a>
</li>
{% for plugin in plugins %}
{% if plugin['page'] %}
<li class="mt-0.5 w-full">
<a class="dark:hover:bg-primary/20 hover:bg-primary/5 hover:rounded-lg dark:text-gray-200 py-1 text-sm ease-nav-brand my-0 mx-2 flex items-center whitespace-nowrap px-4 transition"
href="{{ request.url_root }}plugins/{{ plugin['id'] }}">
<div class="mr-2 flex flex-wrap items-center justify-center rounded-lg bg-center stroke-0 text-center p-1 xl:p-1.5">
{% if plugin['type'] != "pro" %}
{% endfor %}
<!-- end loop paths-->
</ul>
<!-- end default anchor -->
<ul>
<li class="w-full mt-4">
<h6 class="pl-6 ml-2 text-xs font-bold leading-tight uppercase dark:text-gray-400 dark:opacity-100 opacity-60">
PLUGINS PAGE
</h6>
</li>
{% for plugin in plugins %}
{% if plugin['page'] %}
<li class="mt-0.5 w-full">
<a class="dark:hover:bg-primary/20 hover:bg-primary/5 hover:rounded-lg dark:text-gray-200 py-1 text-sm ease-nav-brand my-0 mx-2 flex items-center whitespace-nowrap px-4 transition"
href="{{ request.url_root }}plugins/{{ plugin['id'] }}">
<div class="mr-2 flex flex-wrap items-center justify-center rounded-lg bg-center stroke-0 text-center p-1 xl:p-1.5">
{% if plugin['type'] != "pro" %}
<svg class="fill-gray-500 h-5 w-5 relative"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 384 512">
<path d="M0 64C0 28.7 28.7 0 64 0H224V128c0 17.7 14.3 32 32 32H384V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64zm384 64H256V0L384 128z" />
</svg>
{% endif %}
{% if plugin['type'] == "pro" %}
{% endif %}
{% if plugin['type'] == "pro" %}
<svg class="h-5 w-5 dark:brightness-90 relative"
viewBox="0 0 48 46"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<path class="fill-yellow-500" d="M43.218 28.2327L43.6765 23.971C43.921 21.6973 44.0825 20.1957 43.9557 19.2497L44 19.25C46.071 19.25 47.75 17.5711 47.75 15.5C47.75 13.4289 46.071 11.75 44 11.75C41.929 11.75 40.25 13.4289 40.25 15.5C40.25 16.4366 40.5935 17.2931 41.1613 17.9503C40.346 18.4535 39.2805 19.515 37.6763 21.1128C36.4405 22.3438 35.8225 22.9593 35.1333 23.0548C34.7513 23.1075 34.3622 23.0532 34.0095 22.898C33.373 22.6175 32.9485 21.8567 32.0997 20.335L27.6262 12.3135C27.1025 11.3747 26.6642 10.5889 26.2692 9.95662C27.89 9.12967 29 7.44445 29 5.5C29 2.73857 26.7615 0.5 24 0.5C21.2385 0.5 19 2.73857 19 5.5C19 7.44445 20.11 9.12967 21.7308 9.95662C21.3358 10.589 20.8975 11.3746 20.3738 12.3135L15.9002 20.335C15.0514 21.8567 14.627 22.6175 13.9905 22.898C13.6379 23.0532 13.2487 23.1075 12.8668 23.0548C12.1774 22.9593 11.5595 22.3438 10.3238 21.1128C8.71968 19.515 7.6539 18.4535 6.83882 17.9503C7.4066 17.2931 7.75 16.4366 7.75 15.5C7.75 13.4289 6.07107 11.75 4 11.75C1.92893 11.75 0.25 13.4289 0.25 15.5C0.25 17.5711 1.92893 19.25 4 19.25L4.04428 19.2497C3.91755 20.1957 4.07905 21.6973 4.32362 23.971L4.782 28.2327C5.03645 30.5982 5.24802 32.849 5.50717 34.875H42.4928C42.752 32.849 42.9635 30.5982 43.218 28.2327Z" fill="#1C274C" />
<path class="fill-yellow-500" d="M21.2803 45.5H26.7198C33.8098 45.5 37.3545 45.5 39.7198 43.383C40.7523 42.4588 41.4057 40.793 41.8775 38.625H6.1224C6.59413 40.793 7.24783 42.4588 8.2802 43.383C10.6454 45.5 14.1903 45.5 21.2803 45.5Z" fill="#1C274C" />
</svg>
{% endif %}
</div>
<span class="ml-1 duration-300 opacity-100 pointer-events-none ease">{{ plugin['name'] }}</span>
</a>
</li>
{% endif %}
{% endfor %}
</ul>
<!-- end plugin list -->
</div>
<!-- end top sidebar -->
<!-- end list items -->
<!-- bottom sidebar -->
viewBox="0 0 48 46"
fill="none"
xmlns="http://www.w3.org/2000/svg">
<path class="fill-yellow-500" d="M43.218 28.2327L43.6765 23.971C43.921 21.6973 44.0825 20.1957 43.9557 19.2497L44 19.25C46.071 19.25 47.75 17.5711 47.75 15.5C47.75 13.4289 46.071 11.75 44 11.75C41.929 11.75 40.25 13.4289 40.25 15.5C40.25 16.4366 40.5935 17.2931 41.1613 17.9503C40.346 18.4535 39.2805 19.515 37.6763 21.1128C36.4405 22.3438 35.8225 22.9593 35.1333 23.0548C34.7513 23.1075 34.3622 23.0532 34.0095 22.898C33.373 22.6175 32.9485 21.8567 32.0997 20.335L27.6262 12.3135C27.1025 11.3747 26.6642 10.5889 26.2692 9.95662C27.89 9.12967 29 7.44445 29 5.5C29 2.73857 26.7615 0.5 24 0.5C21.2385 0.5 19 2.73857 19 5.5C19 7.44445 20.11 9.12967 21.7308 9.95662C21.3358 10.589 20.8975 11.3746 20.3738 12.3135L15.9002 20.335C15.0514 21.8567 14.627 22.6175 13.9905 22.898C13.6379 23.0532 13.2487 23.1075 12.8668 23.0548C12.1774 22.9593 11.5595 22.3438 10.3238 21.1128C8.71968 19.515 7.6539 18.4535 6.83882 17.9503C7.4066 17.2931 7.75 16.4366 7.75 15.5C7.75 13.4289 6.07107 11.75 4 11.75C1.92893 11.75 0.25 13.4289 0.25 15.5C0.25 17.5711 1.92893 19.25 4 19.25L4.04428 19.2497C3.91755 20.1957 4.07905 21.6973 4.32362 23.971L4.782 28.2327C5.03645 30.5982 5.24802 32.849 5.50717 34.875H42.4928C42.752 32.849 42.9635 30.5982 43.218 28.2327Z" fill="#1C274C" />
<path class="fill-yellow-500" d="M21.2803 45.5H26.7198C33.8098 45.5 37.3545 45.5 39.7198 43.383C40.7523 42.4588 41.4057 40.793 41.8775 38.625H6.1224C6.59413 40.793 7.24783 42.4588 8.2802 43.383C10.6454 45.5 14.1903 45.5 21.2803 45.5Z" fill="#1C274C" />
</svg>
{% endif %}
</div>
<span class="ml-1 duration-300 opacity-100 pointer-events-none ease">{{ plugin['name'] }}</span>
</a>
</li>
{% endif %}
{% endfor %}
</ul>
<!-- end plugin list -->
</div>
<!-- end top sidebar -->
<!-- end list items -->
<!-- bottom sidebar -->
<div class="flex flex-col justify-end mx-4 mt-2 mb-4">
<!-- dark/light mode -->
<div class="min-h-6 ml-12 my-4 flex justify-start">
@ -251,6 +248,7 @@
<input {% if dark_mode == True %}checked{% endif %}
id="darkMode"
data-dark-toggle
data-mode-link="{{ url_for('darkmode') }}"
class="dark:brightness-125 hover:brightness-75 rounded-10 duration-300 ease-in-out after:rounded-circle after:shadow-2xl after:duration-300 checked:after:translate-x-5.3 h-5 mt-0.5 relative float-left w-10 cursor-pointer appearance-none border border-solid border-gray-200 bg-slate-800/10 bg-none bg-contain bg-left bg-no-repeat align-top transition-all after:absolute after:top-px after:h-4 after:w-4 after:translate-x-px after:bg-white after:content-[''] checked:border-primary checked:bg-primary checked:bg-none checked:bg-right"
type="checkbox" />
<label for="darkMode"

View file

@ -11,20 +11,12 @@
] %}
<div class="h-fit p-4 col-span-12 md:col-span-5 2xl:col-span-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
<h5 class="col-span-12 mb-4 font-bold dark:text-white/90">INFO</h5>
<div role="grid" class="card-detail-container">
{% for info in plugins_info %}
<div role="row"
class="card-detail-item">
<p role="gridcell"
class="card-detail-item-title">
{{ info['name'] }}
</p>
<p role="gridcell"
class="card-detail-item-subtitle">
{{ info['data'] }}
</p>
</div>
{% for info in plugins_info %}
<div role="row" class="card-detail-item">
<p role="gridcell" class="card-detail-item-title">{{ info['name'] }}</p>
<p role="gridcell" class="card-detail-item-subtitle">{{ info['data'] }}</p>
</div>
{% endfor %}
</div>
</div>
@ -155,7 +147,7 @@
</div>
<!-- end filter -->
<div data-plugins-list-container
class="min-h-[55vh] max-h-80 overflow-hidden overflow-y-auto p-4 col-span-12 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
class="min-h-[55vh] max-h-80 overflow-auto p-4 col-span-12 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
<h5 class="mb-4 mt-2 font-bold dark:text-white/90 mx-2">LIST</h5>
<div data-plugins-list class="grid grid-cols-12 gap-3">
{% for plugin in plugins %}

View file

@ -4,7 +4,15 @@
{% set codes = ["all"] %}
{% set reasons = ["all"] %}
{% set countries = ["all"] %}
{% set reasons_count = {} %}
{% set codes_count = {} %}
{% set countries_count = {} %}
{% set methods_count = {} %}
{% for report in reports %}
{% if reasons_count.update({report["reason"]|string : reasons_count.get(report["reason"], 0) + 1}) %}{% endif %}
{% if codes_count.update({report["status"]|string : codes_count.get(report["status"], 0) + 1}) %}{% endif %}
{% if countries_count.update({report["country"]|string : codes_count.get(report["country"], 0) + 1}) %}{% endif %}
{% if methods_count.update({report["method"]|string : methods_count.get(report["method"], 0) + 1}) %}{% endif %}
{% if report["method"] not in methods %}
{% if methods.append(report["method"]) %}{% endif %}
{% endif %}
@ -18,6 +26,31 @@
{% if countries.append(report["country"]) %}{% endif %}
{% endif %}
{% endfor %}
{% set top_reason = {"count": 0, "name": "unknown"} %}
{% for key, value in reasons_count.items() %}
{% if value|int > top_reason["count"]|int %}
{% if top_reason.update({"count": value, "name" : key|string}) %}{% endif %}
{% endif %}
{% endfor %}
{% set top_code = {"count": 0, "name": "unknown"} %}
{% for key, value in codes_count.items() %}
{% if value|int > top_code["count"]|int %}
{% if top_code.update({ "count" : value|string, "name" : key}) %}{% endif %}
{% endif %}
{% endfor %}
{% set top_country = {"count": 0, "name": "unknown"} %}
{% for key, value in countries_count.items() %}
{% if value|int > top_country["count"]|int %}
{% if top_country.update({ "count" : value|string, "name" : key}) %}{% endif %}
{% endif %}
{% endfor %}
{% set top_method = {"count": 0, "name": "unknown"} %}
{% for key, value in methods_count.items() %}
{% if value|int > top_method["count"]|int %}
{% if top_method.update({ "count" : value|string, "name" : key}) %}{% endif %}
{% endif %}
{% endfor %}
{% set total_report = reports|length %}
<div class="{% if reports|length == 0 %}w-full overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words {% else %}hidden{% endif %} ">
<div class="col-span-12 flex flex-col justify-center items-center h-fit">
<svg xmlns="http://www.w3.org/2000/svg"
@ -34,25 +67,20 @@
<!-- info-->
{% if reports|length != 0 %}
{% set reports_info = [
{"name" : "REPORTING TOTAL", "data" : total_report|string},
{"name" : "TOP REASON", "data" : total_reason|string},
{"name" : "TOP STATUS CODE", "data" : top_code|string},
{"name" : "REPORTING TOTAL", "data" : total_report|string or "0"},
{"name" : "TOP METHOD", "data" : top_method['name']|string},
{"name" : "TOP REASON", "data" : top_reason['name']|string},
{"name" : "TOP COUNTRY", "data" : top_country['name']|string},
{"name" : "TOP STATUS CODE", "data" : top_code['name']|string},
] %}
<div class=" h-fit col-span-12 md:col-span-4 3xl:col-span-3 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">
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
<div role="grid" class="card-detail-container">
{% for info in reports_info %}
<div role="row"
class="card-detail-item">
<p role="gridcell"
class="card-detail-item-title">
{{ info['name'] }}
</p>
<p role="gridcell"
class="card-detail-item-subtitle">
{{ info['data'] }}
</p>
</div>
{% for info in reports_info %}
<div role="row" class="card-detail-item">
<p role="gridcell" class="card-detail-item-title">{{ info['name'] }}</p>
<p role="gridcell" class="card-detail-item-subtitle">{{ info['data'] }}</p>
</div>
{% endfor %}
</div>
</div>

View file

@ -48,17 +48,10 @@
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
<div role="grid" class="card-detail-container">
{% for info in services_info %}
<div role="row"
class="card-detail-item">
<p role="gridcell"
class="card-detail-item-title">
{{ info['name'] }}
</p>
<p role="gridcell"
class="card-detail-item-subtitle">
{{ info['data'] }}
</p>
</div>
<div role="row" class="card-detail-item">
<p role="gridcell" class="card-detail-item-title">{{ info['name'] }}</p>
<p role="gridcell" class="card-detail-item-subtitle">{{ info['data'] }}</p>
</div>
{% endfor %}
</div>
</div>

View file

@ -42,130 +42,10 @@
</svg>
</button>
</div>
<div data-services-tabs-select-header class="flex flex-col">
<div class="flex flex-col md:flex-row justify-start w-full items-start md:items-end gap-y-3 gap-x-4">
<div class="w-full sm:min-w-[250px] max-w-[300px]">{% include "settings_tabs_select.html" %}</div>
<div class="flex flex-col sm:flex-row">
<!-- search inpt-->
<div class="sm:mx-2 mb-1 min-w-[200px] flex flex-col relative col-span-12 md:col-span-6">
<h5 class="hidden sm:block my-1 transition duration-300 ease-in-out text-sm sm:text-md font-bold m-0 dark:text-gray-200">
Search
</h5>
<label class="sr-only" for="settings-filter">search</label>
<input type="text"
id="settings-filter"
name="settings-filter"
class="col-span-12 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-3 py-1 font-normal text-gray-700 transition-all placeholder:text-gray-500"
placeholder="keyword"
pattern="(.*?)"
required />
</div>
<!-- end search inpt-->
<!-- type plugin -->
<div class="hidden mx-2 mb-1 min-w-[200px] sm:flex flex-col relative col-span-12 md:col-span-6">
<h5 class="my-1 transition duration-300 ease-in-out text-sm sm:text-md font-bold m-0 dark:text-gray-200">
Type
</h5>
<button aria-controls="filter-type" data-services-setting-select="type" class="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:opacity-90 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 text-sm leading-5.6 ease w-full rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-1.5 py-1 md:px-3 font-normal text-gray-700 transition-all placeholder:text-gray-500">
<span aria-description="current filter state value" id="services-type" data-name="services-type" data-services-setting-select-text="type">all</span>
<!-- chevron -->
<svg data-services-setting-select="type" class="transition-transform h-4 w-4 fill-gray-500" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z"></path>
</svg>
</button>
<!-- end chevron -->
<!-- dropdown-->
<div id="filter-type" role="listbox" data-services-setting-select-dropdown="type" class="mt-1 hidden z-100 absolute flex-col w-full translate-y-16 max-h-[350px] overflow-hidden overflow-y-auto">
<button role="option" data-services-setting-select-dropdown-btn="type" value="all" class="dark:bg-primary bg-primary text-gray-300 border-t rounded-t border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 my-0 relative py-2 px-3 text-left align-middle transition-all rounded-none cursor-pointer leading-normal text-sm ease-in tracking-tight-rem dark:border-slate-600 dark:text-gray-300">
all
</button>
{% include "services_modal_settings_advanced.html" %}
{% include "services_modal_delete.html" %}
<button role="option" data-services-setting-select-dropdown-btn="type" value="core" class=" bg-white dark:bg-slate-700 border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 my-0 relative py-2 px-3 text-left align-middle transition-all rounded-none cursor-pointer leading-normal text-sm ease-in tracking-tight-rem dark:border-slate-600 dark:text-gray-300">
core
</button>
<button role="option" data-services-setting-select-dropdown-btn="type" value="external" class=" bg-white dark:bg-slate-700 border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 my-0 relative py-2 px-3 text-left align-middle transition-all rounded-none cursor-pointer leading-normal text-sm ease-in tracking-tight-rem dark:border-slate-600 dark:text-gray-300">
external
</button>
<button role="option" data-services-setting-select-dropdown-btn="type" value="pro" class=" bg-white dark:bg-slate-700 rounded-b border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 my-0 relative py-2 px-3 text-left align-middle transition-all rounded-none cursor-pointer leading-normal text-sm ease-in tracking-tight-rem dark:border-slate-600 dark:text-gray-300">
pro
</button>
</div>
<!-- end dropdown-->
</div>
<!-- end type plugin-->
</div>
</div>
<div class="w-full min-w-[300px] my-1 sm:my-0">
<hr class="separator" />
</div>
</div>
<div data-services-nomatch
class="hidden w-full overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words">
<div class="col-span-12 flex flex-col justify-center items-center h-fit">
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="mb-2 w-8 h-8 dark:stroke-white/90 stroke-gray-800">
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607ZM10.5 7.5v6m3-3h-6" />
</svg>
<h5 class="font-bold dark:text-white/90 mx-2 text-gray-800">No settings match</h5>
</div>
</div>
<!-- new and edit form -->
<form data-services-modal-form
class="w-full h-[90vh] overflow-auto flex flex-col justify-between"
id="form-new"
method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input type="hidden" id="operation" value="new" name="operation" />
<input type="hidden" value="new" name="OLD_SERVER_NAME" />
<input type="hidden" value="no" name="is_draft" />
{% include "settings_plugins.html" %}
<!-- action button -->
<div class="w-full flex-col items-center justify-center flex mt-10">
<div class="flex justify-center">
<button data-services-modal-close
type="button"
class="dark:bg-slate-800 close-btn mb-4 mr-3 text-base">Close</button>
<button data-services-modal-submit type="submit" class="mb-4 valid-btn">Save</button>
</div>
<!-- end action button-->
<p data-services-modal-error-msg
class="hidden text-red-500 font-bold dark:opacity-80 mb-0 text-center"></p>
</div>
</form>
<!-- end new and edit form -->
<!-- delete form-->
<form data-services-modal-form-delete
class="w-full h-full flex flex-col justify-between"
id="form-delete-server_name"
method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input type="hidden" value="delete" name="operation" />
<input type="hidden" value="no" name="is_draft" />
<input type="hidden" value="" name="SERVER_NAME" />
<div class="flex justify-center">
<p data-services-modal-text
class="text-gray-700 dark:text-gray-300 mx-2 mb-2 mt-8 font-semibold font-sans leading-normal uppercase text-sm">
</p>
</div>
<!-- action button -->
<div class="w-full justify-center flex mt-10">
<button data-services-modal-close
type="button"
class="dark:bg-slate-800 close-btn mb-4 mr-3 text-base">Close</button>
<button type="submit" class="delete-btn mb-4 mr-3 text-base">Delete</button>
</div>
<!-- end action button-->
</form>
<!-- end delete form-->
</div>
</div>
<!-- end modal -->

View file

@ -0,0 +1,24 @@
<!-- delete form-->
<form data-services-modal-form-delete
class="w-full h-full flex flex-col justify-between"
id="form-delete-server_name"
method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input type="hidden" value="delete" name="operation" />
<input type="hidden" value="no" name="is_draft" />
<input type="hidden" value="" name="SERVER_NAME" />
<div class="flex justify-center">
<p data-services-modal-text
class="text-gray-700 dark:text-gray-300 mx-2 mb-2 mt-8 font-semibold font-sans leading-normal uppercase text-sm">
</p>
</div>
<!-- action button -->
<div class="w-full justify-center flex mt-10">
<button data-services-modal-close
type="button"
class="dark:bg-slate-800 close-btn mb-4 mr-3 text-base">Close</button>
<button type="submit" class="delete-btn mb-4 mr-3 text-base">Delete</button>
</div>
<!-- end action button-->
</form>
<!-- end delete form-->

View file

@ -0,0 +1,115 @@
<div data-advanced data-services-tabs-select-header class="flex flex-col">
<div class="flex flex-col md:flex-row justify-start w-full items-start md:items-end gap-y-3 gap-x-4">
<div class="w-full sm:min-w-[250px] max-w-[300px]">{% include "settings_tabs_select.html" %}</div>
<div class="flex flex-col sm:flex-row">
<!-- search inpt-->
<div class="sm:mx-2 mb-1 min-w-[200px] flex flex-col relative col-span-12 md:col-span-6">
<h5 class="hidden sm:block my-1 transition duration-300 ease-in-out text-sm sm:text-md font-bold m-0 dark:text-gray-200">
Search
</h5>
<label class="sr-only" for="settings-filter">search</label>
<input type="text"
id="settings-filter"
name="settings-filter"
class="col-span-12 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-3 py-1 font-normal text-gray-700 transition-all placeholder:text-gray-500"
placeholder="keyword"
pattern="(.*?)"
required />
</div>
<!-- end search inpt-->
<!-- type plugin -->
<div class="hidden mx-2 mb-1 min-w-[200px] sm:flex flex-col relative col-span-12 md:col-span-6">
<h5 class="my-1 transition duration-300 ease-in-out text-sm sm:text-md font-bold m-0 dark:text-gray-200">Type</h5>
<button aria-controls="filter-type"
data-services-setting-select="type"
class="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:opacity-90 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 text-sm leading-5.6 ease w-full rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-1.5 py-1 md:px-3 font-normal text-gray-700 transition-all placeholder:text-gray-500">
<span aria-description="current filter state value"
id="services-type"
data-name="services-type"
data-services-setting-select-text="type">all</span>
<!-- chevron -->
<svg data-services-setting-select="type"
class="transition-transform h-4 w-4 fill-gray-500"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512">
<path d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z">
</path>
</svg>
</button>
<!-- end chevron -->
<!-- dropdown-->
<div id="filter-type"
role="listbox"
data-services-setting-select-dropdown="type"
class="mt-1 hidden z-100 absolute flex-col w-full translate-y-16 max-h-[350px] overflow-hidden overflow-y-auto">
<button role="option"
data-services-setting-select-dropdown-btn="type"
value="all"
class="dark:bg-primary bg-primary text-gray-300 border-t rounded-t border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 my-0 relative py-2 px-3 text-left align-middle transition-all rounded-none cursor-pointer leading-normal text-sm ease-in tracking-tight-rem dark:border-slate-600 dark:text-gray-300">
all
</button>
<button role="option"
data-services-setting-select-dropdown-btn="type"
value="core"
class=" bg-white dark:bg-slate-700 border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 my-0 relative py-2 px-3 text-left align-middle transition-all rounded-none cursor-pointer leading-normal text-sm ease-in tracking-tight-rem dark:border-slate-600 dark:text-gray-300">
core
</button>
<button role="option"
data-services-setting-select-dropdown-btn="type"
value="external"
class=" bg-white dark:bg-slate-700 border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 my-0 relative py-2 px-3 text-left align-middle transition-all rounded-none cursor-pointer leading-normal text-sm ease-in tracking-tight-rem dark:border-slate-600 dark:text-gray-300">
external
</button>
<button role="option"
data-services-setting-select-dropdown-btn="type"
value="pro"
class=" bg-white dark:bg-slate-700 rounded-b border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 my-0 relative py-2 px-3 text-left align-middle transition-all rounded-none cursor-pointer leading-normal text-sm ease-in tracking-tight-rem dark:border-slate-600 dark:text-gray-300">
pro
</button>
</div>
<!-- end dropdown-->
</div>
<!-- end type plugin-->
</div>
</div>
<div class="w-full min-w-[300px] my-1 sm:my-0">
<hr class="separator" />
</div>
</div>
<div data-advanced data-services-nomatch
class="hidden w-full overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words">
<div class="col-span-12 flex flex-col justify-center items-center h-fit">
<svg xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke-width="1.5"
stroke="currentColor"
class="mb-2 w-8 h-8 dark:stroke-white/90 stroke-gray-800">
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607ZM10.5 7.5v6m3-3h-6" />
</svg>
<h5 class="font-bold dark:text-white/90 mx-2 text-gray-800">No settings match</h5>
</div>
</div>
<!-- new and edit form -->
<form data-advanced data-services-modal-form
class="w-full h-[90vh] overflow-auto flex flex-col justify-between"
id="form-new"
method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input type="hidden" id="operation" value="new" name="operation" />
<input type="hidden" value="new" name="OLD_SERVER_NAME" />
<input type="hidden" value="no" name="is_draft" />
{% include "settings_plugins.html" %}
<!-- action button -->
<div class="w-full flex-col items-center justify-center flex mt-10">
<div class="flex justify-center">
<button data-services-modal-close
type="button"
class="dark:bg-slate-800 close-btn mb-4 mr-3 text-base">Close</button>
<button data-services-modal-submit type="submit" class="mb-4 valid-btn">Save</button>
</div>
<!-- end action button-->
<p data-services-modal-error-msg
class="hidden text-red-500 font-bold dark:opacity-80 mb-0 text-center"></p>
</div>
</form>

View file

@ -0,0 +1,35 @@
<div data-simple class="flex flex-col">
<div class="flex flex-col w-full items-end gap-y-3 gap-x-4">
<h2 data-simple-step></h2>
<p data-simple-description></p>
</div>
<div class="w-full min-w-[300px] my-1 sm:my-0">
<hr class="separator" />
</div>
</div>
<!-- new and edit form -->
<form data-simple data-services-simple-modal-form
class="w-full h-[90vh] overflow-auto flex flex-col justify-between"
id="form-simple-new"
method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input type="hidden" id="simple_operation" value="new" name="simple_operation" />
<input type="hidden" value="" name="simple_OLD_SERVER_NAME" />
<input type="hidden" value="no" name="simple_is_draft" />
<input type="hidden" value="yes" name="simple_is_simple_mode" />
<!-- action button -->
<div class="w-full flex-col items-center justify-center flex mt-10">
<div class="flex justify-center">
<button data-services-modal-close
type="button"
class="dark:bg-slate-800 close-btn mb-4 mr-3 text-base">Close</button>
<button data-services-modal-submit type="submit" class="mb-4 valid-btn">Save</button>
</div>
<!-- end action button-->
<p data-simple-services-modal-error-msg
class="hidden text-red-500 font-bold dark:opacity-80 mb-0 text-center"></p>
</div>
</form>

Some files were not shown because too many files have changed in this diff Show more