Merge remote-tracking branch 'origin/master' into russian_locale

# Conflicts:
#	lib/main.dart
This commit is contained in:
Dmitry Nagibin 2024-01-18 20:54:11 +05:00
commit d0e77bc4ff
252 changed files with 19911 additions and 13707 deletions

View file

@ -2,19 +2,9 @@ name: Compile and release beta build
on: on:
workflow_dispatch: workflow_dispatch:
inputs: branches:
version: - beta
description: "Version"
required: true
default: "1.0.0"
beta-number:
description: "Beta number"
required: true
default: "1"
number:
description: "Build number"
required: true
default: "1"
jobs: jobs:
build-android: build-android:
name: Build Android .apk and .aab name: Build Android .apk and .aab
@ -22,7 +12,9 @@ jobs:
env: env:
ANDROID_AAB_RELEASE_PATH: build/app/outputs/bundle/release ANDROID_AAB_RELEASE_PATH: build/app/outputs/bundle/release
ANDROID_APK_RELEASE_PATH: build/app/outputs/apk/release ANDROID_APK_RELEASE_PATH: build/app/outputs/apk/release
VERSION_NAME: ${{ github.event.inputs.version }}-beta.${{ github.event.inputs.beta-number }} outputs:
VERSION_NAME: ${{ steps.save_version.outputs.version_name }}
VERSION_NUMBER: ${{ steps.save_version.outputs.version_number }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
@ -33,8 +25,21 @@ jobs:
run: echo "${{ secrets.KEY_PROPERTIES }}" | base64 --decode > android/key.properties run: echo "${{ secrets.KEY_PROPERTIES }}" | base64 --decode > android/key.properties
- name: Decode .env - name: Decode .env
run: echo "${{ secrets.ENV }}" | base64 --decode > .env run: echo "${{ secrets.ENV }}" | base64 --decode > .env
- name: Update version in YAML - name: Read pubspec.yaml
run: sed -i 's/99.99.99+99/${{ env.VERSION_NAME }}+${{ github.event.inputs.number }}/g' pubspec.yaml uses: adore-me/read-yaml@v1.0.0
id: read_pubspec
with:
file: './pubspec.yaml'
key-path: '["version"]'
- name: Save version on env variable
id: save_version
run: |
version=${{ steps.read_pubspec.outputs.data }}
IFS='+'
read -r -a split <<< "$version"
echo "VERSION_NAME=$(echo ${split[0]})" >> $GITHUB_ENV
echo "version_name=${split[0]}" >> $GITHUB_OUTPUT
echo "version_number=${split[1]}" >> $GITHUB_OUTPUT
- name: Update KeyStore password in gradle properties - name: Update KeyStore password in gradle properties
run: sed -i 's/#{KEYSTORE_PASS}#/${{ secrets.KEYSTORE_PASS }}/g' android/key.properties run: sed -i 's/#{KEYSTORE_PASS}#/${{ secrets.KEYSTORE_PASS }}/g' android/key.properties
- name: Update KeyStore key password in gradle properties - name: Update KeyStore key password in gradle properties
@ -69,8 +74,9 @@ jobs:
name: Release beta build to GitHub name: Release beta build to GitHub
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build-android] needs: [build-android]
env: env:
VERSION_NAME: ${{ github.event.inputs.version }}-beta.${{ github.event.inputs.beta-number }} VERSION_NAME: ${{ needs.build-android.outputs.VERSION_NAME }}
VERSION_NUMBER: ${{ needs.build-android.outputs.VERSION_NUMBER }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
@ -87,7 +93,7 @@ jobs:
with: with:
artifacts: "releases/*" artifacts: "releases/*"
token: ${{ secrets.GH_TOKEN }} token: ${{ secrets.GH_TOKEN }}
tag: '${{ env.VERSION_NAME }}_(${{ github.event.inputs.number }})' tag: '${{ env.VERSION_NAME }}_(${{ env.VERSION_NUMBER }})'
name: v${{ env.VERSION_NAME }} name: v${{ env.VERSION_NAME }}
draft: true draft: true
prerelease: true prerelease: true
@ -97,7 +103,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build-android] needs: [build-android]
env: env:
VERSION_NAME: ${{ github.event.inputs.version }}-beta.${{ github.event.inputs.beta-number }} VERSION_NAME: ${{ needs.build-android.outputs.VERSION_NAME }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:

View file

@ -2,15 +2,9 @@ name: Compile and release production build
on: on:
workflow_dispatch: workflow_dispatch:
inputs: branches:
version: - master
description: "Version"
required: true
default: "1.0.0"
number:
description: "Build number"
required: true
default: "1"
jobs: jobs:
build-android: build-android:
name: Build Android .apk and .aab name: Build Android .apk and .aab
@ -18,6 +12,9 @@ jobs:
env: env:
ANDROID_AAB_RELEASE_PATH: build/app/outputs/bundle/release ANDROID_AAB_RELEASE_PATH: build/app/outputs/bundle/release
ANDROID_APK_RELEASE_PATH: build/app/outputs/apk/release ANDROID_APK_RELEASE_PATH: build/app/outputs/apk/release
outputs:
VERSION_NAME: ${{ steps.save_version.outputs.version_name }}
VERSION_NUMBER: ${{ steps.save_version.outputs.version_number }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Decode android/app/keystore.jks - name: Decode android/app/keystore.jks
@ -26,8 +23,21 @@ jobs:
run: echo "${{ secrets.KEY_PROPERTIES }}" | base64 --decode > android/key.properties run: echo "${{ secrets.KEY_PROPERTIES }}" | base64 --decode > android/key.properties
- name: Decode .env - name: Decode .env
run: echo "${{ secrets.ENV }}" | base64 --decode > .env run: echo "${{ secrets.ENV }}" | base64 --decode > .env
- name: Update version in YAML - name: Read pubspec.yaml
run: sed -i 's/99.99.99+99/${{ github.event.inputs.version }}+${{ github.event.inputs.number }}/g' pubspec.yaml uses: adore-me/read-yaml@v1.0.0
id: read_pubspec
with:
file: './pubspec.yaml'
key-path: '["version"]'
- name: Save version on env variable
id: save_version
run: |
version=${{ steps.read_pubspec.outputs.data }}
IFS='+'
read -r -a split <<< "$version"
echo "VERSION_NAME=$(echo ${split[0]})" >> $GITHUB_ENV
echo "version_name=${split[0]}" >> $GITHUB_OUTPUT
echo "version_number=${split[1]}" >> $GITHUB_OUTPUT
- name: Update KeyStore password in gradle properties - name: Update KeyStore password in gradle properties
run: sed -i 's/#{KEYSTORE_PASS}#/${{ secrets.KEYSTORE_PASS }}/g' android/key.properties run: sed -i 's/#{KEYSTORE_PASS}#/${{ secrets.KEYSTORE_PASS }}/g' android/key.properties
- name: Update KeyStore key password in gradle properties - name: Update KeyStore key password in gradle properties
@ -44,37 +54,67 @@ jobs:
- run: flutter build apk --release - run: flutter build apk --release
- run: flutter build appbundle --release - run: flutter build appbundle --release
- name: Rename apk - name: Rename apk
run: mv $ANDROID_APK_RELEASE_PATH/app-release.apk $ANDROID_APK_RELEASE_PATH/AdGuardHomeManager_${{ github.event.inputs.version }}_Android.apk run: mv $ANDROID_APK_RELEASE_PATH/app-release.apk $ANDROID_APK_RELEASE_PATH/AdGuardHomeManager_${{ env.VERSION_NAME }}_Android.apk
- name: Rename aab - name: Rename aab
run: mv $ANDROID_AAB_RELEASE_PATH/app-release.aab $ANDROID_AAB_RELEASE_PATH/AdGuardHomeManager_${{ github.event.inputs.version }}_Android.aab run: mv $ANDROID_AAB_RELEASE_PATH/app-release.aab $ANDROID_AAB_RELEASE_PATH/AdGuardHomeManager_${{ env.VERSION_NAME }}_Android.aab
- name: Copy apk to project root - name: Copy apk to project root
run: cp $ANDROID_APK_RELEASE_PATH/AdGuardHomeManager_${{ github.event.inputs.version }}_Android.apk AdGuardHomeManager_${{ github.event.inputs.version }}_Android.apk run: cp $ANDROID_APK_RELEASE_PATH/AdGuardHomeManager_${{ env.VERSION_NAME }}_Android.apk AdGuardHomeManager_${{ env.VERSION_NAME }}_Android.apk
- name: Copy aab to project root - name: Copy aab to project root
run: cp $ANDROID_AAB_RELEASE_PATH/AdGuardHomeManager_${{ github.event.inputs.version }}_Android.aab AdGuardHomeManager_${{ github.event.inputs.version }}_Android.aab run: cp $ANDROID_AAB_RELEASE_PATH/AdGuardHomeManager_${{ env.VERSION_NAME }}_Android.aab AdGuardHomeManager_${{ env.VERSION_NAME }}_Android.aab
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: android name: android
path: | path: |
AdGuardHomeManager_${{ github.event.inputs.version }}_Android.aab AdGuardHomeManager_${{ env.VERSION_NAME }}_Android.aab
AdGuardHomeManager_${{ github.event.inputs.version }}_Android.apk AdGuardHomeManager_${{ env.VERSION_NAME }}_Android.apk
build-macos: build-macos:
name: Build macOS .dmg name: Build macOS .dmg
runs-on: macos-latest runs-on: macos-latest
env: env:
MACOS_APP_RELEASE_PATH: build/macos/Build/Products/Release MACOS_APP_RELEASE_PATH: build/macos/Build/Products/Release
outputs:
VERSION_NAME: ${{ steps.save_version.outputs.version_name }}
VERSION_NUMBER: ${{ steps.save_version.outputs.version_number }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Decode .env - name: Decode .env
run: echo "${{ secrets.ENV }}" | base64 --decode > .env run: echo "${{ secrets.ENV }}" | base64 --decode > .env
- name: Update version in YAML - name: Read pubspec.yaml
run: sed -i '' 's/99.99.99+99/${{ github.event.inputs.version }}+${{ github.event.inputs.number }}/g' pubspec.yaml uses: adore-me/read-yaml@v1.0.0
id: read_pubspec
with:
file: './pubspec.yaml'
key-path: '["version"]'
- name: Save version on env variable
id: save_version
run: |
version=${{ steps.read_pubspec.outputs.data }}
IFS='+'
read -r -a split <<< "$version"
echo "VERSION_NAME=$(echo ${split[0]})" >> $GITHUB_ENV
echo "version_name=${split[0]}" >> $GITHUB_OUTPUT
echo "version_number=${split[1]}" >> $GITHUB_OUTPUT
- uses: subosito/flutter-action@v2 - uses: subosito/flutter-action@v2
with: with:
channel: "stable" channel: "stable"
- run: flutter clean - run: flutter clean
- run: flutter pub get - run: flutter pub get
- run: flutter build macos --release - run: flutter build macos --release
- name: Install the Apple certificate and sign the application
env:
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PWD: ${{ secrets.APPLE_CERTIFICATE_PWD }}
APPLE_KEYCHAIN_PWD: ${{ secrets.APPLE_KEYCHAIN_PWD }}
APPLE_IDENTITY_ID: ${{ secrets.APPLE_IDENTITY_ID }}
run: |
echo "$APPLE_CERTIFICATE" | base64 --decode > certificate.p12
security create-keychain -p $APPLE_KEYCHAIN_PWD build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p $APPLE_KEYCHAIN_PWD build.keychain
security import certificate.p12 -k build.keychain -P $APPLE_CERTIFICATE_PWD -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k $APPLE_KEYCHAIN_PWD build.keychain
/usr/bin/codesign --force -s "$APPLE_IDENTITY_ID" $MACOS_APP_RELEASE_PATH/AdGuard\ Home\ Manager.app -v
- name: Create folder to build dmg - name: Create folder to build dmg
run: mkdir $MACOS_APP_RELEASE_PATH/AdGuard\ Home\ Manager run: mkdir $MACOS_APP_RELEASE_PATH/AdGuard\ Home\ Manager
- name: Copy app into folder - name: Copy app into folder
@ -82,25 +122,41 @@ jobs:
- name: Generate symbolic link to Applications dir - name: Generate symbolic link to Applications dir
run: ln -s /Applications $MACOS_APP_RELEASE_PATH/AdGuard\ Home\ Manager run: ln -s /Applications $MACOS_APP_RELEASE_PATH/AdGuard\ Home\ Manager
- name: Generate dmg - name: Generate dmg
run: hdiutil create -srcfolder $MACOS_APP_RELEASE_PATH/AdGuard\ Home\ Manager $MACOS_APP_RELEASE_PATH/AdGuardHomeManager_${{ github.event.inputs.version }}_macOS_Universal.dmg run: hdiutil create -srcfolder $MACOS_APP_RELEASE_PATH/AdGuard\ Home\ Manager $MACOS_APP_RELEASE_PATH/AdGuardHomeManager_${{ env.VERSION_NAME }}_macOS_Universal.dmg
- name: Copy dmg to project root - name: Copy dmg to project root
run: cp $MACOS_APP_RELEASE_PATH/AdGuardHomeManager_${{ github.event.inputs.version }}_macOS_Universal.dmg AdGuardHomeManager_${{ github.event.inputs.version }}_macOS_Universal.dmg run: cp $MACOS_APP_RELEASE_PATH/AdGuardHomeManager_${{ env.VERSION_NAME }}_macOS_Universal.dmg AdGuardHomeManager_${{ env.VERSION_NAME }}_macOS_Universal.dmg
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: macos name: macos
path: AdGuardHomeManager_${{ github.event.inputs.version }}_macOS_Universal.dmg path: AdGuardHomeManager_${{ env.VERSION_NAME }}_macOS_Universal.dmg
build-linux: build-linux:
name: Build Linux .tar.gz and .deb name: Build Linux .tar.gz and .deb
runs-on: ubuntu-latest runs-on: ubuntu-latest
outputs:
VERSION_NAME: ${{ steps.save_version.outputs.version_name }}
VERSION_NUMBER: ${{ steps.save_version.outputs.version_number }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Decode .env - name: Decode .env
run: echo "${{ secrets.ENV }}" | base64 --decode > .env run: echo "${{ secrets.ENV }}" | base64 --decode > .env
- name: Update version in pubspec.yaml - name: Read pubspec.yaml
run: sed -i 's/99.99.99+99/${{ github.event.inputs.version }}+${{ github.event.inputs.number }}/g' pubspec.yaml uses: adore-me/read-yaml@v1.0.0
id: read_pubspec
with:
file: './pubspec.yaml'
key-path: '["version"]'
- name: Save version on env variable
id: save_version
run: |
version=${{ steps.read_pubspec.outputs.data }}
IFS='+'
read -r -a split <<< "$version"
echo "VERSION_NAME=$(echo ${split[0]})" >> $GITHUB_ENV
echo "version_name=${split[0]}" >> $GITHUB_OUTPUT
echo "version_number=${split[1]}" >> $GITHUB_OUTPUT
- name: Update version in debian.yaml - name: Update version in debian.yaml
run: sed -i 's/<REPLACE_VERSION_NUMBER_ACTIONS>/${{ github.event.inputs.version }}/g' debian/debian.yaml run: sed -i 's/<REPLACE_VERSION_NUMBER_ACTIONS>/${{ env.VERSION_NAME }}/g' debian/debian.yaml
- name: Update dependencies list - name: Update dependencies list
run: sudo apt-get update run: sudo apt-get update
- name: Install dependencies - name: Install dependencies
@ -116,7 +172,7 @@ jobs:
- name: Generate .deb package - name: Generate .deb package
run: flutter_to_debian run: flutter_to_debian
- name: Move .deb package to project root - name: Move .deb package to project root
run: mv debian/packages/AdGuardHomeManager_${{ github.event.inputs.version }}_amd64.deb AdGuardHomeManager_${{ github.event.inputs.version }}_Linux_amd64.deb run: mv build/linux/x64/release/debian/AdGuardHomeManager_${{ env.VERSION_NAME }}_amd64.deb AdGuardHomeManager_${{ env.VERSION_NAME }}_Linux_amd64.deb
- name: Generate .tar.gz package - name: Generate .tar.gz package
uses: a7ul/tar-action@v1.1.3 uses: a7ul/tar-action@v1.1.3
id: compress id: compress
@ -127,30 +183,46 @@ jobs:
./data ./data
./lib ./lib
./AdGuardHomeManager ./AdGuardHomeManager
outPath: AdGuardHomeManager_${{ github.event.inputs.version }}_Linux.tar.gz outPath: AdGuardHomeManager_${{ env.VERSION_NAME }}_Linux.tar.gz
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: linux name: linux
path: | path: |
AdGuardHomeManager_${{ github.event.inputs.version }}_Linux_amd64.deb AdGuardHomeManager_${{ env.VERSION_NAME }}_Linux_amd64.deb
AdGuardHomeManager_${{ github.event.inputs.version }}_Linux.tar.gz AdGuardHomeManager_${{ env.VERSION_NAME }}_Linux.tar.gz
build-windows: build-windows:
name: Build Windows installer name: Build Windows installer
runs-on: windows-latest runs-on: windows-latest
outputs:
VERSION_NAME: ${{ steps.save_version.outputs.version_name }}
VERSION_NUMBER: ${{ steps.save_version.outputs.version_number }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Decode .env - name: Decode .env
shell: pwsh shell: pwsh
run: | run: |
[IO.File]::WriteAllBytes('.env', [Convert]::FromBase64String('${{ secrets.ENV }}')) [IO.File]::WriteAllBytes('.env', [Convert]::FromBase64String('${{ secrets.ENV }}'))
- name: Update version in pubspec.yaml - name: Read pubspec.yaml
uses: adore-me/read-yaml@v1.0.0
id: read_pubspec
with:
file: './pubspec.yaml'
key-path: '["version"]'
- name: Save version on env variable
shell: bash shell: bash
run: sed -i 's/99.99.99+99/${{ github.event.inputs.version }}+${{ github.event.inputs.number }}/g' pubspec.yaml id: save_version
run: |
version=${{ steps.read_pubspec.outputs.data }}
IFS='+'
read -r -a split <<< "$version"
echo "VERSION_NAME=$(echo ${split[0]})" >> $GITHUB_ENV
echo "version_name=${split[0]}" >> $GITHUB_OUTPUT
echo "version_number=${split[1]}" >> $GITHUB_OUTPUT
- name: Update version in innosetup config file - name: Update version in innosetup config file
shell: pwsh shell: pwsh
run: | run: |
(Get-Content windows/innosetup_installer_builder.iss) -replace '<REPLACE_VERSION_ACTIONS>', '${{ github.event.inputs.version }}' | Out-File -encoding ASCII windows/innosetup_installer_builder.iss (Get-Content windows/innosetup_installer_builder.iss) -replace '<REPLACE_VERSION_ACTIONS>', '${{ env.VERSION_NAME }}' | Out-File -encoding ASCII windows/innosetup_installer_builder.iss
- uses: subosito/flutter-action@v2 - uses: subosito/flutter-action@v2
with: with:
channel: "stable" channel: "stable"
@ -160,16 +232,19 @@ jobs:
- name: Build installer witn innosetup - name: Build installer witn innosetup
run: iscc /Q windows/innosetup_installer_builder.iss run: iscc /Q windows/innosetup_installer_builder.iss
- name: Move installer file to root directory - name: Move installer file to root directory
run: move build/windows/aghm_installer.exe AdGuardHomeManager_${{ github.event.inputs.version }}_Windows_x64.exe run: move build/windows/aghm_installer.exe AdGuardHomeManager_${{ env.VERSION_NAME }}_Windows_x64.exe
- name: Upload artifact - name: Upload artifact
uses: actions/upload-artifact@v3 uses: actions/upload-artifact@v3
with: with:
name: windows name: windows
path: AdGuardHomeManager_${{ github.event.inputs.version }}_Windows_x64.exe path: AdGuardHomeManager_${{ env.VERSION_NAME }}_Windows_x64.exe
release-builds-github: release-builds-github:
name: Release builds to GitHub name: Release builds to GitHub
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build-android, build-macos, build-linux, build-windows] needs: [build-android, build-macos, build-linux, build-windows]
env:
VERSION_NAME: ${{ needs.build-android.outputs.VERSION_NAME }}
VERSION_NUMBER: ${{ needs.build-android.outputs.VERSION_NUMBER }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Create builds directory - name: Create builds directory
@ -199,8 +274,8 @@ jobs:
with: with:
artifacts: "releases/*" artifacts: "releases/*"
token: ${{ secrets.GH_TOKEN }} token: ${{ secrets.GH_TOKEN }}
tag: '${{ github.event.inputs.version }}_(${{ github.event.inputs.number }})' tag: '${{ env.VERSION_NAME }}_(${{ env.VERSION_NUMBER }})'
name: v${{ github.event.inputs.version }} name: v${{ env.VERSION_NAME }}
draft: true draft: true
prerelease: false prerelease: false
commit: ${{ github.sha }} commit: ${{ github.sha }}
@ -208,6 +283,8 @@ jobs:
name: Release Android build to the Google Play Store name: Release Android build to the Google Play Store
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build-android, build-macos, build-linux, build-windows] needs: [build-android, build-macos, build-linux, build-windows]
env:
VERSION_NAME: ${{ needs.build-android.outputs.VERSION_NAME }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- name: Download Android artifacts - name: Download Android artifacts
@ -219,7 +296,7 @@ jobs:
with: with:
serviceAccountJsonPlainText: ${{ secrets.PLAYSTORE_ACCOUNT_KEY }} serviceAccountJsonPlainText: ${{ secrets.PLAYSTORE_ACCOUNT_KEY }}
packageName: com.jgeek00.adguard_home_manager packageName: com.jgeek00.adguard_home_manager
releaseFiles: AdGuardHomeManager_${{ github.event.inputs.version }}_Android.aab releaseFiles: AdGuardHomeManager_${{ env.VERSION_NAME }}_Android.aab
track: production track: production
status: draft status: draft
releaseName: ${{ github.event.inputs.version }} releaseName: ${{ env.VERSION_NAME }}

4
.gitignore vendored
View file

@ -46,4 +46,6 @@ app.*.map.json
/android/app/profile /android/app/profile
/android/app/release /android/app/release
/debian/packages /debian/packages
untranslated.json

View file

@ -44,18 +44,28 @@ On [this repository](https://github.com/JuanRodenas/Pihole_list) you can find a
## Generate production build ## Generate production build
<ul> <ul>
<li>
<b>Prerequisites</b>
<ol>
<li>Open <code>pubspec.yaml</code> and change the version name and the version number.</li>
<li>Run <code>flutter clean</code>.</li>
<li>Run <code>flutter pub get</code>.</li>
</ol>
</li>
<li>
<b>Android</b>
<ol>
<li>Make sure you have your <code>key.properties</code> file at <code>android/</code>, with all the required values of your signing key correctly set up.</li>
<li>Make sure you have your keystore file at <code>android/app</code>.</li>
<li>Run <code>flutter build apk --release</code> to compile the APK.</li>
<li>The .apk package is located at <code>build/app/outputs/flutter-apk/app-release.apk</code>.</li>
</ol>
</li>
<li> <li>
<b>macOS</b> <b>macOS</b>
<ol> <ol>
<li>flutter clean</li> <li>Run <code>flutter build macos --release</code> to compile the production build.</li>
<li>flutter pub get</li> <li>The .app package is located at <code>build/macos/Build/Products/Release/AdGuard Home Manager.app</code>.</li>
<li>flutter build macos --release</li>
<li>Open macos/Runner.xcworkspace on Xcode</li>
<li>Make sure all the pods have the minimum deployment version at 10.14</li>
<li>Select Runner > Targets Runner</li>
<li>Make sure the Version and Build numbers are correct</li>
<li>Click on Product menu and on Archive</li>
<li>Select the first on the list and click on Distribute app, select Copy App and click on Next</li>
</ol> </ol>
</li> </li>
<li> <li>
@ -67,24 +77,22 @@ On [this repository](https://github.com/JuanRodenas/Pihole_list) you can find a
</ol> </ol>
<b>Build</b> <b>Build</b>
<ol> <ol>
<li>Open debian.yaml file inside debian/ and update the version number</li> <li>Open <code>debian.yaml</code> file inside debian/ and update the version number</li>
<li>run <code>rps build linux</code></li> <li>run <code>rps build linux</code></li>
<li>The .tar.gz is at build/linux/x64/release/bundle</li> <li>The .tar.gz is at <code>build/linux/x64/release/bundle</code></li>
<li>The .deb package is at debian/packages</li> <li>The .deb package is at <code>build/linux/x64/release/debian/</code></li>
</ol> </ol>
</ul> </ul>
</li> </li>
<li> <li>
<b>Windows</b> <b>Windows</b>
<ol> <ol>
<li>flutter clean</li> <li>Run <code>flutter build windows --release</code>.</li>
<li>flutter pub get</li>
<li>flutter build windows</li>
<li>Open Inno Setup Compiler application and load the script</li> <li>Open Inno Setup Compiler application and load the script</li>
<li>The script is located at windows/innosetup_installer_builder.iss</li> <li>The script is located at <code>windows/innosetup_installer_builder.iss</code></li>
<li>Update the version number and save the changes</li> <li>Update the version number and save the changes</li>
<li>Click on the Compile button</li> <li>Click on the Compile button</li>
<li>The installer will be generated at build/windows/aghm_installer.exe</li> <li>The installer will be generated at <code>build/windows/aghm_installer.exe</code>.</li>
</ol> </ol>
</li> </li>
</ul> </ul>
@ -119,6 +127,9 @@ On [this repository](https://github.com/JuanRodenas/Pihole_list) you can find a
- [async](https://pub.dev/packages/async) - [async](https://pub.dev/packages/async)
- [sentry flutter](https://pub.dev/packages/sentry_flutter) - [sentry flutter](https://pub.dev/packages/sentry_flutter)
- [flutter dotenv](https://pub.dev/packages/flutter_dotenv) - [flutter dotenv](https://pub.dev/packages/flutter_dotenv)
- [flutter reorderable list](https://pub.dev/packages/flutter_reorderable_list)
- [pie chart](https://pub.dev/packages/pie_chart)
- [segmented button slide](https://pub.dev/packages/segmented_button_slide)
<br> <br>

View file

@ -15,7 +15,8 @@
android:label="AdGuard Home Manager" android:label="AdGuard Home Manager"
android:name="${applicationName}" android:name="${applicationName}"
android:requestLegacyExternalStorage="true" android:requestLegacyExternalStorage="true"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher"
android:enableOnBackInvokedCallback="true">
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:exported="true" android:exported="true"

View file

@ -0,0 +1,4 @@
storePassword= # keystore password #
keyPassword= # keystore key password #
keyAlias= # key alias #
storeFile= # ./keystore-file-name.jks #

1
devtools_options.yaml Normal file
View file

@ -0,0 +1 @@
extensions:

0
ios/.gitignore vendored Normal file → Executable file
View file

0
ios/Flutter/AppFrameworkInfo.plist Normal file → Executable file
View file

0
ios/Flutter/Debug.xcconfig Normal file → Executable file
View file

0
ios/Flutter/Release.xcconfig Normal file → Executable file
View file

0
ios/Podfile Normal file → Executable file
View file

52
ios/Podfile.lock Normal file → Executable file
View file

@ -11,11 +11,35 @@ PODS:
- FMDB/standard (2.7.5) - FMDB/standard (2.7.5)
- package_info_plus (0.4.5): - package_info_plus (0.4.5):
- Flutter - Flutter
- sqflite (0.0.2): - Sentry/HybridSDK (8.15.2):
- SentryPrivate (= 8.15.2)
- sentry_flutter (0.0.1):
- Flutter
- FlutterMacOS
- Sentry/HybridSDK (= 8.15.2)
- SentryPrivate (8.15.2)
- sqflite (0.0.3):
- Flutter - Flutter
- FMDB (>= 2.7.5) - FMDB (>= 2.7.5)
- sqlite3 (3.44.0):
- sqlite3/common (= 3.44.0)
- sqlite3/common (3.44.0)
- sqlite3/fts5 (3.44.0):
- sqlite3/common
- sqlite3/perf-threadsafe (3.44.0):
- sqlite3/common
- sqlite3/rtree (3.44.0):
- sqlite3/common
- sqlite3_flutter_libs (0.0.1):
- Flutter
- sqlite3 (~> 3.44.0)
- sqlite3/fts5
- sqlite3/perf-threadsafe
- sqlite3/rtree
- store_checker (0.0.1): - store_checker (0.0.1):
- Flutter - Flutter
- url_launcher_ios (0.0.1):
- Flutter
DEPENDENCIES: DEPENDENCIES:
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
@ -23,12 +47,18 @@ DEPENDENCIES:
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
- flutter_web_browser (from `.symlinks/plugins/flutter_web_browser/ios`) - flutter_web_browser (from `.symlinks/plugins/flutter_web_browser/ios`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`)
- sqflite (from `.symlinks/plugins/sqflite/ios`) - sqflite (from `.symlinks/plugins/sqflite/ios`)
- sqlite3_flutter_libs (from `.symlinks/plugins/sqlite3_flutter_libs/ios`)
- store_checker (from `.symlinks/plugins/store_checker/ios`) - store_checker (from `.symlinks/plugins/store_checker/ios`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
SPEC REPOS: SPEC REPOS:
trunk: trunk:
- FMDB - FMDB
- Sentry
- SentryPrivate
- sqlite3
EXTERNAL SOURCES: EXTERNAL SOURCES:
device_info_plus: device_info_plus:
@ -41,21 +71,33 @@ EXTERNAL SOURCES:
:path: ".symlinks/plugins/flutter_web_browser/ios" :path: ".symlinks/plugins/flutter_web_browser/ios"
package_info_plus: package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios" :path: ".symlinks/plugins/package_info_plus/ios"
sentry_flutter:
:path: ".symlinks/plugins/sentry_flutter/ios"
sqflite: sqflite:
:path: ".symlinks/plugins/sqflite/ios" :path: ".symlinks/plugins/sqflite/ios"
sqlite3_flutter_libs:
:path: ".symlinks/plugins/sqlite3_flutter_libs/ios"
store_checker: store_checker:
:path: ".symlinks/plugins/store_checker/ios" :path: ".symlinks/plugins/store_checker/ios"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
SPEC CHECKSUMS: SPEC CHECKSUMS:
device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6
Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854
flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef
flutter_web_browser: 7bccaafbb0c5b8862afe7bcd158f15557109f61f flutter_web_browser: 7bccaafbb0c5b8862afe7bcd158f15557109f61f
FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a
package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85
sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 Sentry: 6f5742b4c47c17c9adcf265f6f328cf4a0ed1923
sentry_flutter: 2c309a1d4b45e59d02cfa15795705687f1e2081b
SentryPrivate: b2f7996f37781080f04a946eb4e377ff63c64195
sqflite: 31f7eba61e3074736dff8807a9b41581e4f7f15a
sqlite3: 6e2d4a4879854d0ec86b476bf3c3e30870bac273
sqlite3_flutter_libs: eb769059df0356dc52ddda040f09cacc9391a7cf
store_checker: 359c5051d9ec30ff0a8fa39eb5ec9df021bb745d store_checker: 359c5051d9ec30ff0a8fa39eb5ec9df021bb745d
url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b
PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3 PODFILE CHECKSUM: ef19549a9bc3046e7bb7d2fab4d021637c0c58a3
COCOAPODS: 1.11.2 COCOAPODS: 1.14.3

44
ios/Runner.xcodeproj/project.pbxproj Normal file → Executable file
View file

@ -68,7 +68,6 @@
65533F0C0783FDE34AE79B0A /* Pods-Runner.release.xcconfig */, 65533F0C0783FDE34AE79B0A /* Pods-Runner.release.xcconfig */,
69C2CC4A6DE17506FC5C0F13 /* Pods-Runner.profile.xcconfig */, 69C2CC4A6DE17506FC5C0F13 /* Pods-Runner.profile.xcconfig */,
); );
name = Pods;
path = Pods; path = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
@ -156,7 +155,8 @@
97C146E61CF9000F007C117D /* Project object */ = { 97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
LastUpgradeCheck = 1300; BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1430;
ORGANIZATIONNAME = ""; ORGANIZATIONNAME = "";
TargetAttributes = { TargetAttributes = {
97C146ED1CF9000F007C117D = { 97C146ED1CF9000F007C117D = {
@ -222,6 +222,7 @@
files = ( files = (
); );
inputPaths = ( inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
); );
name = "Thin Binary"; name = "Thin Binary";
outputPaths = ( outputPaths = (
@ -324,6 +325,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
@ -334,6 +336,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -342,7 +345,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@ -360,15 +363,22 @@
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 38Z3B9TJTR; DEVELOPMENT_TEAM = 38Z3B9TJTR;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "AdGuard Home";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.jgeek00.adguard_home_manager; PRODUCT_BUNDLE_IDENTIFIER = com.jgeek00.adguardHomeManager;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Profile; name = Profile;
@ -396,6 +406,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
@ -406,6 +417,7 @@
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
@ -420,7 +432,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -451,6 +463,7 @@
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES;
@ -461,6 +474,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -469,7 +483,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 11.0; IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos; SUPPORTED_PLATFORMS = iphoneos;
@ -489,16 +503,23 @@
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 38Z3B9TJTR; DEVELOPMENT_TEAM = 38Z3B9TJTR;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "AdGuard Home";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.jgeek00.adguard_home_manager; PRODUCT_BUNDLE_IDENTIFIER = com.jgeek00.adguardHomeManager;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Debug; name = Debug;
@ -512,15 +533,22 @@
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 38Z3B9TJTR; DEVELOPMENT_TEAM = 38Z3B9TJTR;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "AdGuard Home";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.jgeek00.adguard_home_manager; PRODUCT_BUNDLE_IDENTIFIER = com.jgeek00.adguardHomeManager;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
}; };
name = Release; name = Release;

0
ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata generated Normal file → Executable file
View file

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1300" LastUpgradeVersion = "1430"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"

0
ios/Runner.xcworkspace/contents.xcworkspacedata generated Normal file → Executable file
View file

View file

View file

0
ios/Runner/AppDelegate.swift Normal file → Executable file
View file

View file

View file

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 526 B

After

Width:  |  Height:  |  Size: 526 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1,005 B

After

Width:  |  Height:  |  Size: 1,005 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 773 B

After

Width:  |  Height:  |  Size: 773 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1,005 B

After

Width:  |  Height:  |  Size: 1,005 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Before After
Before After

0
ios/Runner/Assets.xcassets/LaunchBackground.imageset/Contents.json vendored Normal file → Executable file
View file

0
ios/Runner/Assets.xcassets/LaunchBackground.imageset/background.png vendored Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 68 B

After

Width:  |  Height:  |  Size: 68 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 70 B

After

Width:  |  Height:  |  Size: 70 B

Before After
Before After

0
ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json vendored Normal file → Executable file
View file

0
ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png vendored Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Before After
Before After

0
ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png vendored Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Before After
Before After

0
ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png vendored Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before After
Before After

0
ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImageDark.png vendored Normal file → Executable file
View file

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 7.3 KiB

After

Width:  |  Height:  |  Size: 7.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Before After
Before After

0
ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md vendored Normal file → Executable file
View file

0
ios/Runner/Base.lproj/LaunchScreen.storyboard Normal file → Executable file
View file

0
ios/Runner/Base.lproj/Main.storyboard Normal file → Executable file
View file

98
ios/Runner/Info.plist Normal file → Executable file
View file

@ -1,53 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>CFBundleDevelopmentRegion</key> <key>CADisableMinimumFrameDurationOnPhone</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <true/>
<key>CFBundleDisplayName</key> <key>CFBundleDevelopmentRegion</key>
<string>AdGuard Home Manager</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key> <key>CFBundleDisplayName</key>
<string>$(EXECUTABLE_NAME)</string> <string>AdGuard Home</string>
<key>CFBundleIdentifier</key> <key>CFBundleExecutable</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleIdentifier</key>
<string>6.0</string> <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleName</key> <key>CFBundleInfoDictionaryVersion</key>
<string>adguard_home_manager</string> <string>6.0</string>
<key>CFBundlePackageType</key> <key>CFBundleName</key>
<string>APPL</string> <string>adguardHomeManager</string>
<key>CFBundleShortVersionString</key> <key>CFBundlePackageType</key>
<string>$(FLUTTER_BUILD_NAME)</string> <string>APPL</string>
<key>CFBundleSignature</key> <key>CFBundleShortVersionString</key>
<string>????</string> <string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleVersion</key> <key>CFBundleSignature</key>
<string>$(FLUTTER_BUILD_NUMBER)</string> <string>????</string>
<key>LSRequiresIPhoneOS</key> <key>CFBundleVersion</key>
<true/> <string>$(FLUTTER_BUILD_NUMBER)</string>
<key>UILaunchStoryboardName</key> <key>LSRequiresIPhoneOS</key>
<string>LaunchScreen</string> <true/>
<key>UIMainStoryboardFile</key> <key>UIApplicationSupportsIndirectInputEvents</key>
<string>Main</string> <true/>
<key>UISupportedInterfaceOrientations</key> <key>UILaunchStoryboardName</key>
<array> <string>LaunchScreen</string>
<string>UIInterfaceOrientationPortrait</string> <key>UIMainStoryboardFile</key>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>Main</string>
<string>UIInterfaceOrientationLandscapeRight</string> <key>UIStatusBarHidden</key>
</array> <false/>
<key>UISupportedInterfaceOrientations~ipad</key> <key>UISupportedInterfaceOrientations</key>
<array> <array>
<string>UIInterfaceOrientationPortrait</string> <string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string> <string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeLeft</string> <string>UIInterfaceOrientationLandscapeRight</string>
<string>UIInterfaceOrientationLandscapeRight</string> </array>
</array> <key>UISupportedInterfaceOrientations~ipad</key>
<key>UIViewControllerBasedStatusBarAppearance</key> <array>
<false/> <string>UIInterfaceOrientationPortrait</string>
<key>CADisableMinimumFrameDurationOnPhone</key> <string>UIInterfaceOrientationPortraitUpsideDown</string>
<true/> <string>UIInterfaceOrientationLandscapeLeft</string>
<key>UIApplicationSupportsIndirectInputEvents</key> <string>UIInterfaceOrientationLandscapeRight</string>
<true/> </array>
<key>UIStatusBarHidden</key> <key>UIViewControllerBasedStatusBarAppearance</key>
<false/> <false/>
</dict> </dict>
</plist> </plist>

0
ios/Runner/Runner-Bridging-Header.h Normal file → Executable file
View file

View file

@ -1,3 +1,4 @@
arb-dir: lib/l10n arb-dir: lib/l10n
template-arb-file: app_en.arb template-arb-file: app_en.arb
output-localization-file: app_localizations.dart output-localization-file: app_localizations.dart
untranslated-messages-file: untranslated.json

View file

@ -1,116 +0,0 @@
// ignore_for_file: use_build_context_synchronously, depend_on_referenced_packages
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:animations/animations.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:provider/provider.dart';
import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:flutter/services.dart';
import 'package:adguard_home_manager/widgets/bottom_nav_bar.dart';
import 'package:adguard_home_manager/widgets/menu_bar.dart';
import 'package:adguard_home_manager/widgets/update_modal.dart';
import 'package:adguard_home_manager/widgets/navigation_rail.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart';
import 'package:adguard_home_manager/functions/check_app_updates.dart';
import 'package:adguard_home_manager/functions/open_url.dart';
import 'package:adguard_home_manager/models/app_screen.dart';
import 'package:adguard_home_manager/config/app_screens.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart';
class Base extends StatefulWidget {
const Base({Key? key}) : super(key: key);
@override
State<Base> createState() => _BaseState();
}
class _BaseState extends State<Base> with WidgetsBindingObserver {
int selectedScreen = 0;
@override
void initState() {
WidgetsBinding.instance.addObserver(this);
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_) async {
final appConfigProvider = Provider.of<AppConfigProvider>(context, listen: false);
final result = await checkAppUpdates(
currentBuildNumber: appConfigProvider.getAppInfo!.buildNumber,
installationSource: appConfigProvider.installationSource,
setUpdateAvailable: appConfigProvider.setAppUpdatesAvailable,
isBeta: appConfigProvider.getAppInfo!.version.contains('beta'),
);
if (result != null && appConfigProvider.doNotRememberVersion != result.tagName) {
await showDialog(
context: context,
builder: (context) => UpdateModal(
gitHubRelease: result,
onDownload: (link, version) => openUrl(link),
),
);
}
});
}
@override
Widget build(BuildContext context) {
final serversProvider = Provider.of<ServersProvider>(context);
final appConfigProvider = Provider.of<AppConfigProvider>(context);
final width = MediaQuery.of(context).size.width;
List<AppScreen> screens = serversProvider.selectedServer != null
? screensServerConnected
: screensSelectServer;
if (kDebugMode && dotenv.env['ENABLE_SENTRY'] == "true") {
Sentry.captureMessage("Debug mode");
}
return CustomMenuBar(
child: AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarBrightness: Theme.of(context).brightness == Brightness.light
? Brightness.light
: Brightness.dark,
statusBarIconBrightness: Theme.of(context).brightness == Brightness.light
? Brightness.dark
: Brightness.light,
systemNavigationBarColor: Theme.of(context).scaffoldBackgroundColor,
systemNavigationBarIconBrightness: Theme.of(context).brightness == Brightness.light
? Brightness.dark
: Brightness.light,
),
child: Scaffold(
body: Row(
children: [
if (width > 900) const SideNavigationRail(),
Expanded(
child: PageTransitionSwitcher(
duration: const Duration(milliseconds: 200),
transitionBuilder: (
(child, primaryAnimation, secondaryAnimation) => FadeThroughTransition(
animation: primaryAnimation,
secondaryAnimation: secondaryAnimation,
child: child,
)
),
child: screens[appConfigProvider.selectedScreen].body,
),
),
],
),
bottomNavigationBar: width <= 900
? const BottomNavBar()
: null,
)
),
);
}
}

View file

@ -0,0 +1,216 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:adguard_home_manager/models/server.dart';
enum ExceptionType { socket, timeout, handshake, http, unknown }
class HttpResponse {
final bool successful;
final String? body;
final int? statusCode;
final ExceptionType? exception;
const HttpResponse({
required this.successful,
required this.body,
required this.statusCode,
this.exception,
});
}
String getConnectionString({
required Server server,
required String urlPath,
}) {
return "${server.connectionMethod}://${server.domain}${server.port != null ? ':${server.port}' : ""}${server.path ?? ""}/control$urlPath";
}
class HttpRequestClient {
static Future<HttpResponse> get({
required String urlPath,
required Server server,
int timeout = 10,
}) async{
final String connectionString = getConnectionString(server: server, urlPath: urlPath);
try {
HttpClient httpClient = HttpClient();
HttpClientRequest request = await httpClient.getUrl(Uri.parse(connectionString));
if (server.authToken != null) {
request.headers.set('Authorization', 'Basic ${server.authToken}');
}
HttpClientResponse response = await request.close().timeout(
Duration(seconds: timeout)
);
String reply = await response.transform(utf8.decoder).join();
httpClient.close();
return HttpResponse(
successful: response.statusCode >= 400 ? false : true,
body: reply,
statusCode: response.statusCode
);
} on SocketException {
return const HttpResponse(
successful: false,
body: null,
statusCode: null,
exception: ExceptionType.socket
);
} on TimeoutException {
return const HttpResponse(
successful: false,
body: null,
statusCode: null,
exception: ExceptionType.timeout
);
} on HandshakeException {
return const HttpResponse(
successful: false,
body: null,
statusCode: null,
exception: ExceptionType.handshake
);
} on HttpException {
return const HttpResponse(
successful: false,
body: null,
statusCode: null,
exception: ExceptionType.http
);
} catch (e) {
return const HttpResponse(
successful: false,
body: null,
statusCode: null,
exception: ExceptionType.unknown
);
}
}
static Future<HttpResponse> post({
required String urlPath,
required Server server,
dynamic body,
int timeout = 10,
}) async{
final String connectionString = getConnectionString(server: server, urlPath: urlPath);
try {
HttpClient httpClient = HttpClient();
HttpClientRequest request = await httpClient.postUrl(Uri.parse(connectionString));
if (server.authToken != null) {
request.headers.set('Authorization', 'Basic ${server.authToken}');
}
request.headers.set('content-type', 'application/json');
request.add(utf8.encode(json.encode(body)));
HttpClientResponse response = await request.close().timeout(
Duration(seconds: timeout)
);
String reply = await response.transform(utf8.decoder).join();
httpClient.close();
return HttpResponse(
successful: response.statusCode >= 400 ? false : true,
body: reply,
statusCode: response.statusCode
);
} on SocketException {
return const HttpResponse(
successful: false,
body: null,
statusCode: null,
exception: ExceptionType.socket
);
} on TimeoutException {
return const HttpResponse(
successful: false,
body: null,
statusCode: null,
exception: ExceptionType.timeout
);
} on HttpException {
return const HttpResponse(
successful: false,
body: null,
statusCode: null,
exception: ExceptionType.http
);
} on HandshakeException {
return const HttpResponse(
successful: false,
body: null,
statusCode: null,
exception: ExceptionType.handshake
);
} catch (e) {
return const HttpResponse(
successful: false,
body: null,
statusCode: null,
exception: ExceptionType.unknown
);
}
}
static Future<HttpResponse> put({
required String urlPath,
required Server server,
dynamic body,
int timeout = 10,
}) async{
final String connectionString = getConnectionString(server: server, urlPath: urlPath);
try {
HttpClient httpClient = HttpClient();
HttpClientRequest request = await httpClient.putUrl(Uri.parse(connectionString));
if (server.authToken != null) {
request.headers.set('Authorization', 'Basic ${server.authToken}');
}
request.headers.set('content-type', 'application/json');
request.add(utf8.encode(json.encode(body)));
HttpClientResponse response = await request.close().timeout(
Duration(seconds: timeout)
);
String reply = await response.transform(utf8.decoder).join();
httpClient.close();
return HttpResponse(
successful: response.statusCode >= 400 ? false : true,
body: reply,
statusCode: response.statusCode
);
} on SocketException {
return const HttpResponse(
successful: false,
body: null,
statusCode: null,
exception: ExceptionType.socket
);
} on TimeoutException {
return const HttpResponse(
successful: false,
body: null,
statusCode: null,
exception: ExceptionType.timeout
);
} on HttpException {
return const HttpResponse(
successful: false,
body: null,
statusCode: null,
exception: ExceptionType.http
);
} on HandshakeException {
return const HttpResponse(
successful: false,
body: null,
statusCode: null,
exception: ExceptionType.handshake
);
} catch (e) {
return const HttpResponse(
successful: false,
body: null,
statusCode: null,
exception: ExceptionType.unknown
);
}
}
}

View file

@ -1,20 +1,14 @@
import 'package:adguard_home_manager/config/globals.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:adguard_home_manager/widgets/process_dialog.dart'; import 'package:adguard_home_manager/widgets/process_dialog.dart';
class ProcessModal { class ProcessModal {
late BuildContext context;
ProcessModal({
required this.context
});
void open(String message) async { void open(String message) async {
await Future.delayed(const Duration(seconds: 0), () => { await Future.delayed(const Duration(seconds: 0), () => {
showDialog( showDialog(
context: context, context: globalNavigatorKey.currentContext!,
builder: (c) { builder: (ctx) {
context = c;
return ProcessDialog( return ProcessDialog(
message: message, message: message,
); );
@ -26,6 +20,6 @@ class ProcessModal {
} }
void close() { void close() {
Navigator.pop(context); Navigator.pop(globalNavigatorKey.currentContext!);
} }
} }

View file

@ -1,10 +1,10 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:adguard_home_manager/screens/filters/filters.dart';
import 'package:adguard_home_manager/screens/logs/logs.dart';
import 'package:adguard_home_manager/screens/connect/connect.dart';
import 'package:adguard_home_manager/screens/home/home.dart';
import 'package:adguard_home_manager/screens/clients/clients.dart'; import 'package:adguard_home_manager/screens/clients/clients.dart';
import 'package:adguard_home_manager/screens/connect/connect.dart';
import 'package:adguard_home_manager/screens/filters/filters.dart';
import 'package:adguard_home_manager/screens/home/home.dart';
import 'package:adguard_home_manager/screens/logs/logs.dart';
import 'package:adguard_home_manager/screens/settings/settings.dart'; import 'package:adguard_home_manager/screens/settings/settings.dart';
import 'package:adguard_home_manager/models/app_screen.dart'; import 'package:adguard_home_manager/models/app_screen.dart';
@ -13,12 +13,12 @@ List<AppScreen> screensSelectServer = [
const AppScreen( const AppScreen(
name: "connect", name: "connect",
icon: Icons.link_rounded, icon: Icons.link_rounded,
body: Connect(), child: Connect()
), ),
const AppScreen( const AppScreen(
name: "settings", name: "settings",
icon: Icons.settings_rounded, icon: Icons.settings_rounded,
body: Settings() child: Settings()
) )
]; ];
@ -26,26 +26,26 @@ List<AppScreen> screensServerConnected = [
const AppScreen( const AppScreen(
name: "home", name: "home",
icon: Icons.home_rounded, icon: Icons.home_rounded,
body: Home(), child: Home()
), ),
const AppScreen( const AppScreen(
name: "clients", name: "clients",
icon: Icons.devices, icon: Icons.devices,
body: Clients() child: Clients()
), ),
const AppScreen( const AppScreen(
name: "logs", name: "logs",
icon: Icons.list_alt_rounded, icon: Icons.list_alt_rounded,
body: Logs(), child: Logs()
), ),
const AppScreen( const AppScreen(
name: "filters", name: "filters",
icon: Icons.shield_rounded, icon: Icons.shield_rounded,
body: Filters(), child: Filters()
), ),
const AppScreen( const AppScreen(
name: "settings", name: "settings",
icon: Icons.settings_rounded, icon: Icons.settings_rounded,
body: Settings() child: Settings()
) )
]; ];

View file

@ -1,3 +1,4 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
final GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>(); final GlobalKey<ScaffoldMessengerState> scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
final GlobalKey<NavigatorState> globalNavigatorKey = GlobalKey<NavigatorState>();

View file

@ -5,7 +5,9 @@ import 'package:adguard_home_manager/constants/enums.dart';
final List<HomeTopItems> homeTopItemsDefaultOrder = [ final List<HomeTopItems> homeTopItemsDefaultOrder = [
HomeTopItems.queriedDomains, HomeTopItems.queriedDomains,
HomeTopItems.blockedDomains, HomeTopItems.blockedDomains,
HomeTopItems.recurrentClients HomeTopItems.recurrentClients,
HomeTopItems.topUpstreams,
HomeTopItems.avgUpstreamResponseTime
]; ];
final String homeTopItemsDefaultOrderString = jsonEncode( final String homeTopItemsDefaultOrderString = jsonEncode(

View file

@ -0,0 +1,4 @@
class MinimumServerVersion {
static const String stable = "v0.107.28";
static const String beta = "v0.108.0-b.33";
}

1
lib/config/sizes.dart Normal file
View file

@ -0,0 +1 @@
const double desktopBreakpoint = 1000;

View file

@ -15,7 +15,6 @@ ThemeData lightTheme(ColorScheme? dynamicColorScheme) => ThemeData(
textColor: dynamicColorScheme != null ? dynamicColorScheme.onSurfaceVariant : const Color.fromRGBO(117, 117, 117, 1), textColor: dynamicColorScheme != null ? dynamicColorScheme.onSurfaceVariant : const Color.fromRGBO(117, 117, 117, 1),
iconColor: dynamicColorScheme != null ? dynamicColorScheme.onSurfaceVariant : const Color.fromRGBO(117, 117, 117, 1), iconColor: dynamicColorScheme != null ? dynamicColorScheme.onSurfaceVariant : const Color.fromRGBO(117, 117, 117, 1),
), ),
androidOverscrollIndicator: AndroidOverscrollIndicator.stretch,
); );
ThemeData darkTheme(ColorScheme? dynamicColorScheme) => ThemeData( ThemeData darkTheme(ColorScheme? dynamicColorScheme) => ThemeData(
@ -34,7 +33,6 @@ ThemeData darkTheme(ColorScheme? dynamicColorScheme) => ThemeData(
textColor: dynamicColorScheme != null ? dynamicColorScheme.onSurfaceVariant : const Color.fromRGBO(187, 187, 187, 1), textColor: dynamicColorScheme != null ? dynamicColorScheme.onSurfaceVariant : const Color.fromRGBO(187, 187, 187, 1),
iconColor: dynamicColorScheme != null ? dynamicColorScheme.onSurfaceVariant : const Color.fromRGBO(187, 187, 187, 1), iconColor: dynamicColorScheme != null ? dynamicColorScheme.onSurfaceVariant : const Color.fromRGBO(187, 187, 187, 1),
), ),
androidOverscrollIndicator: AndroidOverscrollIndicator.stretch,
); );
ThemeData lightThemeOldVersions(MaterialColor primaryColor) => ThemeData( ThemeData lightThemeOldVersions(MaterialColor primaryColor) => ThemeData(
@ -53,7 +51,6 @@ ThemeData lightThemeOldVersions(MaterialColor primaryColor) => ThemeData(
iconColor: Color.fromRGBO(117, 117, 117, 1), iconColor: Color.fromRGBO(117, 117, 117, 1),
), ),
brightness: Brightness.light, brightness: Brightness.light,
androidOverscrollIndicator: AndroidOverscrollIndicator.stretch
); );
ThemeData darkThemeOldVersions(MaterialColor primaryColor) => ThemeData( ThemeData darkThemeOldVersions(MaterialColor primaryColor) => ThemeData(
@ -75,5 +72,4 @@ ThemeData darkThemeOldVersions(MaterialColor primaryColor) => ThemeData(
iconColor: Color.fromRGBO(187, 187, 187, 1), iconColor: Color.fromRGBO(187, 187, 187, 1),
), ),
brightness: Brightness.dark, brightness: Brightness.dark,
androidOverscrollIndicator: AndroidOverscrollIndicator.stretch
); );

View file

@ -1,2 +1,2 @@
enum LoadStatus { loading, loaded, error } enum LoadStatus { loading, loaded, error }
enum HomeTopItems { queriedDomains, blockedDomains, recurrentClients } enum HomeTopItems { queriedDomains, blockedDomains, recurrentClients, topUpstreams, avgUpstreamResponseTime }

View file

@ -0,0 +1,31 @@
class RoutesNames {
static const String connect = "/connect";
static const String home = "/home";
static const String queriedDomains = "/home/queried-domains";
static const String blockedDomains = "/home/blocked-domains";
static const String recurrentClients = "/home/recurrent-clients";
static const String clients = "/clients";
static const String clientsList = "/clients/list";
static const String clientPlaceholder = "/clients/list/placeholder";
static const String client = "/clients/list:id";
static const String logs = "/logs";
static const String filters = "/filters";
static const String settings = "/settings";
static const String safeSearch = "/settings/safe-search";
static const String accessSettings = "/settings/access-settigs";
static const String dhcpSettings = "/settings/dhcp-settings";
static const String dnsSettings = "/settings/dns-settings";
static const String encryptionSettings = "/settings/encryption-settings";
static const String dnsRewrites = "/settings/dns-rewrites";
static const String serverUpdates = "/settings/server-updates";
static const String serverInfo = "/settings/server-info";
static const String customization = "/settings/customization";
static const String servers = "/settings/servers";
static const String generalSettings = "/settings/general-settings";
static const String advancedSettings = "/settings/advanced-settings";
}

View file

@ -3,6 +3,7 @@ class Urls {
static const String gitHub = "https://github.com/JGeek00/adguard-home-manager"; static const String gitHub = "https://github.com/JGeek00/adguard-home-manager";
static const String customRuleDocs = "https://kb.adguard.com/en/general/how-to-create-your-own-ad-filters"; static const String customRuleDocs = "https://kb.adguard.com/en/general/how-to-create-your-own-ad-filters";
static const String getReleasesGitHub = "https://api.github.com/repos/JGeek00/adguard-home-manager/releases"; static const String getReleasesGitHub = "https://api.github.com/repos/JGeek00/adguard-home-manager/releases";
static const String getLatestReleaseGitHub = "https://api.github.com/repos/JGeek00/adguard-home-manager/releases/latest";
static const String adGuardHomeReleasesTags = "https://api.github.com/repos/AdGuardTeam/AdGuardHome/releases/tags"; static const String adGuardHomeReleasesTags = "https://api.github.com/repos/AdGuardTeam/AdGuardHome/releases/tags";
static const String googleSearchUrl = "https://www.google.com/search"; static const String googleSearchUrl = "https://www.google.com/search";
static const String connectionInstructions = "https://github.com/JGeek00/adguard-home-manager/wiki/Create-a-connection"; static const String connectionInstructions = "https://github.com/JGeek00/adguard-home-manager/wiki/Create-a-connection";

View file

@ -3,8 +3,8 @@ import 'dart:io';
import 'package:store_checker/store_checker.dart'; import 'package:store_checker/store_checker.dart';
import 'package:adguard_home_manager/functions/compare_versions.dart'; import 'package:adguard_home_manager/functions/compare_versions.dart';
import 'package:adguard_home_manager/services/external_requests.dart';
import 'package:adguard_home_manager/models/github_release.dart'; import 'package:adguard_home_manager/models/github_release.dart';
import 'package:adguard_home_manager/services/http_requests.dart';
Future<GitHubRelease?> checkAppUpdates({ Future<GitHubRelease?> checkAppUpdates({
required String currentBuildNumber, required String currentBuildNumber,
@ -12,21 +12,27 @@ Future<GitHubRelease?> checkAppUpdates({
required Source installationSource, required Source installationSource,
required bool isBeta required bool isBeta
}) async { }) async {
final result = await checkAppUpdatesGitHub(); var result = isBeta
? await ExternalRequests.getReleasesGitHub()
: await ExternalRequests.getReleaseData();
if (result.successful == true) {
late GitHubRelease gitHubRelease;
if (isBeta) {
gitHubRelease = (result.content as List<GitHubRelease>).firstWhere((r) => r.prerelease == true);
}
else {
gitHubRelease = result.content as GitHubRelease;
}
if (result['result'] == 'success') {
final update = gitHubUpdateExists( final update = gitHubUpdateExists(
currentBuildNumber: currentBuildNumber, currentBuildNumber: currentBuildNumber,
gitHubReleases: result['body'], gitHubRelease: gitHubRelease,
isBeta: isBeta isBeta: isBeta
); );
print(update);
if (update == true) { if (update == true) {
final release = isBeta == true setUpdateAvailable(gitHubRelease);
? result['body'].firstWhere((release) => release.prerelease == true)
: result['body'].firstWhere((release) => release.prerelease == false);
setUpdateAvailable(release);
if (Platform.isAndroid) { if (Platform.isAndroid) {
if ( if (
@ -34,7 +40,7 @@ Future<GitHubRelease?> checkAppUpdates({
installationSource == Source.IS_INSTALLED_FROM_PLAY_PACKAGE_INSTALLER || installationSource == Source.IS_INSTALLED_FROM_PLAY_PACKAGE_INSTALLER ||
installationSource == Source.UNKNOWN installationSource == Source.UNKNOWN
) { ) {
return release; return gitHubRelease;
} }
else { else {
return null; return null;
@ -44,7 +50,7 @@ Future<GitHubRelease?> checkAppUpdates({
return null; return null;
} }
else { else {
return release; return gitHubRelease;
} }
} }
else { else {

View file

@ -4,27 +4,20 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/services/api_client.dart';
import 'package:adguard_home_manager/classes/process_modal.dart'; import 'package:adguard_home_manager/classes/process_modal.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart'; import 'package:adguard_home_manager/providers/servers_provider.dart';
import 'package:adguard_home_manager/models/server.dart'; import 'package:adguard_home_manager/models/server.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart';
Future<bool> clearDnsCache(BuildContext context, Server server) async { Future<ApiResponse> clearDnsCache(BuildContext context, Server server) async {
final serversProvider = Provider.of<ServersProvider>(context, listen: false); final serversProvider = Provider.of<ServersProvider>(context, listen: false);
final ProcessModal processModal = ProcessModal(context: context); final ProcessModal processModal = ProcessModal();
processModal.open(AppLocalizations.of(context)!.clearingDnsCache); processModal.open(AppLocalizations.of(context)!.clearingDnsCache);
final result = await serversProvider.apiClient!.resetDnsCache(); final result = await serversProvider.apiClient2!.resetDnsCache();
processModal.close(); processModal.close();
if (result['result'] == 'success') { return result;
return true;
}
else {
final appConfigProvider = Provider.of<AppConfigProvider>(context, listen: false);
appConfigProvider.addLog(result['log']);
return false;
}
} }

View file

@ -151,15 +151,11 @@ bool serverVersionIsAhead({
bool gitHubUpdateExists({ bool gitHubUpdateExists({
required String currentBuildNumber, required String currentBuildNumber,
required List<GitHubRelease> gitHubReleases, required GitHubRelease gitHubRelease,
required bool isBeta required bool isBeta
}) { }) {
final release = isBeta == true
? gitHubReleases.firstWhere((release) => release.prerelease == true)
: gitHubReleases.firstWhere((release) => release.prerelease == false);
final versionNumberRegex = RegExp(r'\(\d+\)'); final versionNumberRegex = RegExp(r'\(\d+\)');
final releaseNumberExtractedMatches = versionNumberRegex.allMatches(release.tagName); final releaseNumberExtractedMatches = versionNumberRegex.allMatches(gitHubRelease.tagName);
if (releaseNumberExtractedMatches.isNotEmpty) { if (releaseNumberExtractedMatches.isNotEmpty) {
final releaseNumberExtracted = releaseNumberExtractedMatches.first.group(0); final releaseNumberExtracted = releaseNumberExtractedMatches.first.group(0);
@ -181,12 +177,12 @@ bool gitHubUpdateExists({
} }
} }
else { else {
Sentry.captureMessage("Invalid release number. Tagname: ${release.tagName}"); Sentry.captureMessage("Invalid release number. Tagname: ${gitHubRelease.tagName}");
return false; return false;
} }
} }
else { else {
Sentry.captureMessage("No matches. ${release.tagName}"); Sentry.captureMessage("No matches. ${gitHubRelease.tagName}");
return false; return false;
} }
} }

View file

@ -0,0 +1,5 @@
import 'package:adguard_home_manager/config/sizes.dart';
bool isDesktop(double width) {
return width > desktopBreakpoint;
}

View file

@ -4,6 +4,7 @@
"connect": "Connect", "connect": "Connect",
"servers": "Servers", "servers": "Servers",
"createConnection": "Create connection", "createConnection": "Create connection",
"editConnection": "Edit connection",
"name": "Name", "name": "Name",
"ipDomain": "IP address or domain", "ipDomain": "IP address or domain",
"path": "Path", "path": "Path",
@ -56,10 +57,10 @@
"serverStatusNotRefreshed": "Server status could not be refreshed", "serverStatusNotRefreshed": "Server status could not be refreshed",
"loadingStatus": "Loading status...", "loadingStatus": "Loading status...",
"errorLoadServerStatus": "Server status could not be loaded", "errorLoadServerStatus": "Server status could not be loaded",
"topQueriedDomains": "Top queried domains", "topQueriedDomains": "Queried domains",
"viewMore": "View more", "viewMore": "View more",
"topClients": "Top clients", "topClients": "Clients",
"topBlockedDomains": "Top blocked domains", "topBlockedDomains": "Blocked domains",
"appSettings": "App settings", "appSettings": "App settings",
"theme": "Theme", "theme": "Theme",
"light": "Light", "light": "Light",
@ -170,7 +171,7 @@
"dnsQueries": "DNS queries", "dnsQueries": "DNS queries",
"average": "Average", "average": "Average",
"blockedFilters": "Blocked by filters", "blockedFilters": "Blocked by filters",
"malwarePhisingBlocked": "Blocked malware/phising", "malwarePhishingBlocked": "Blocked malware/phishing",
"blockedAdultWebsites": "Blocked adult websites", "blockedAdultWebsites": "Blocked adult websites",
"generalSettings": "General settings", "generalSettings": "General settings",
"generalSettingsDescription": "Various different settings", "generalSettingsDescription": "Various different settings",
@ -316,6 +317,7 @@
"deletingRule": "Deleting rule...", "deletingRule": "Deleting rule...",
"enablingList": "Enabling list...", "enablingList": "Enabling list...",
"disablingList": "Disabling list...", "disablingList": "Disabling list...",
"savingList": "Saving list...",
"disableFiltering": "Disable filtering", "disableFiltering": "Disable filtering",
"enablingFiltering": "Enabling filtering...", "enablingFiltering": "Enabling filtering...",
"disablingFiltering": "Disabling filtering...", "disablingFiltering": "Disabling filtering...",
@ -649,7 +651,7 @@
"october": "October", "october": "October",
"november": "November", "november": "November",
"december": "December", "december": "December",
"malwarePhising": "Malware/phising", "malwarePhishing": "Malware/phishing",
"queries": "Queries", "queries": "Queries",
"adultSites": "Adult sites", "adultSites": "Adult sites",
"quickFilters": "Quick filters", "quickFilters": "Quick filters",
@ -660,5 +662,69 @@
"topItemsOrderDescription": "Order the home screen top items lists", "topItemsOrderDescription": "Order the home screen top items lists",
"topItemsReorderInfo": "Hold and swipe an item to reorder it.", "topItemsReorderInfo": "Hold and swipe an item to reorder it.",
"discardChanges": "Discard changes", "discardChanges": "Discard changes",
"discardChangesDescription": "Are you sure you want to discard the changes?" "discardChangesDescription": "Are you sure you want to discard the changes?",
"others": "Others",
"showChart": "Show chart",
"hideChart": "Hide chart",
"showTopItemsChart": "Show top items chart",
"showTopItemsChartDescription": "Shows by default the ring chart on the top items sections. Only affects to the mobile view.",
"openMenu": "Open menu",
"closeMenu": "Close menu",
"openListUrl": "Open list URL",
"selectionMode": "Selection mode",
"enableDisableSelected": "Enable or disable selected items",
"deleteSelected": "Delete selected items",
"deleteSelectedLists": "Delete selected lists",
"allSelectedListsDeletedSuccessfully": "All selected lists have been deleted successfully.",
"deletionResult": "Deletion result",
"deletingLists": "Deleting lists...",
"failedElements": "Failed elements",
"processingLists": "Processing lists...",
"enableDisableResult": "Enable or disable result",
"selectedListsEnabledDisabledSuccessfully": "All selected lists have been enabled or disabled successfully",
"sslWarning": "If you are using an HTTPS connection with a self signed certificate, make sure to enable \"Don't check SSL certificate\" at Settings > Advanced settings.",
"unsupportedServerVersion": "Unsupported server version",
"unsupportedServerVersionMessage": "Your AdGuard Home server version is too old and is not supported by AdGuard Home Manager. You will need to upgrade your AdGuard Home server to a newer version to use this application.",
"yourVersion": "Your version: {version}",
"minimumRequiredVersion": "Minimum required version: {version}",
"topUpstreams": "Top upstreams",
"averageUpstreamResponseTime": "Average upstream response time",
"dhcpNotAvailable": "The DHCP server is not available.",
"osServerInstalledIncompatible": "The OS where the server is installed is not compatible with this feature.",
"resetSettings": "Reset settings",
"resetEncryptionSettingsDescription": "Are you sure you want to reset to default values the encryption settings?",
"resettingConfig": "Resetting configuration...",
"configurationResetSuccessfully": "Configuration resetted successfully",
"configurationResetError": "The configuration couldn't be resetted",
"testUpstreamDnsServers": "Test upstream DNS servers",
"errorTestUpstreamDns": "Error when testing upstream DNS servers.",
"useCustomIpEdns": "Use custom IP for EDNS",
"useCustomIpEdnsDescription": "Allow to use custom IP for EDNS",
"sortingOptions": "Sorting options",
"fromHighestToLowest": "From highest to lowest",
"fromLowestToHighest": "From lowest to highest",
"queryLogsAndStatistics": "Query logs and statistics",
"ignoreClientQueryLog": "Ignore this client in query log",
"ignoreClientStatistics": "Ignore this client in statistics",
"savingChanges": "Saving changes...",
"fallbackDnsServers": "Fallback DNS servers",
"fallbackDnsServersDescription": "Configure fallback DNS servers",
"fallbackDnsServersInfo": "List of fallback DNS servers used when upstream DNS servers are not responding. The syntax is the same as in the main upstreams field above.",
"noFallbackDnsAdded": "No fallback DNS servers added.",
"blockedResponseTtl": "Blocked response TTL",
"blockedResponseTtlDescription": "Specifies for how many seconds the clients should cache a filtered response",
"invalidValue": "Invalid value",
"noDataChart": "There's no data to display this chart.",
"noData": "No data",
"unblockClient": "Unblock client",
"blockingClient": "Blocking client...",
"unblockingClient": "Unblocking client...",
"upstreamDnsCacheConfiguration": "Configuración de la caché DNS upstream",
"enableDnsCachingClient": "Enable DNS caching for this client",
"dnsCacheSize": "DNS cache size",
"nameInvalid": "Name is required",
"oneIdentifierRequired": "At least one identifier is required",
"dnsCacheNumber": "DNS cache size must be a number",
"errors": "Errors",
"redirectHttpsWarning": "If you have enabled \"Redirect to HTTPS automatically\" on your AdGuard Home server, you must select an HTTPS connection and use the HTTPS port of your server."
} }

View file

@ -4,6 +4,7 @@
"connect": "Conectar", "connect": "Conectar",
"servers": "Servidores", "servers": "Servidores",
"createConnection": "Crear conexión", "createConnection": "Crear conexión",
"editConnection": "Editar conexión",
"name": "Nombre", "name": "Nombre",
"ipDomain": "Dirección IP o dominio", "ipDomain": "Dirección IP o dominio",
"path": "Ruta", "path": "Ruta",
@ -56,10 +57,10 @@
"serverStatusNotRefreshed": "No se ha podido actualizar el estado del servidor", "serverStatusNotRefreshed": "No se ha podido actualizar el estado del servidor",
"loadingStatus": "Cargando estado...", "loadingStatus": "Cargando estado...",
"errorLoadServerStatus": "Error al cargar el estado", "errorLoadServerStatus": "Error al cargar el estado",
"topQueriedDomains": "Top dominios solicitados", "topQueriedDomains": "Dominios solicitados",
"viewMore": "Ver más", "viewMore": "Ver más",
"topClients": "Top clientes recurrentes", "topClients": "Clientes recurrentes",
"topBlockedDomains": "Top dominios bloqueados", "topBlockedDomains": "Dominios bloqueados",
"appSettings": "Ajustes de la app", "appSettings": "Ajustes de la app",
"theme": "Tema", "theme": "Tema",
"light": "Claro", "light": "Claro",
@ -170,7 +171,7 @@
"dnsQueries": "Consultas DNS", "dnsQueries": "Consultas DNS",
"average": "Promedio", "average": "Promedio",
"blockedFilters": "Bloqueado por filtros", "blockedFilters": "Bloqueado por filtros",
"malwarePhisingBlocked": "Malware/phising bloqueado", "malwarePhishingBlocked": "Malware/phising bloqueado",
"blockedAdultWebsites": "Sitios para adultos bloqueados", "blockedAdultWebsites": "Sitios para adultos bloqueados",
"generalSettings": "Ajustes generales", "generalSettings": "Ajustes generales",
"generalSettingsDescription": "Varios ajustes generales", "generalSettingsDescription": "Varios ajustes generales",
@ -316,6 +317,7 @@
"deletingRule": "Eliminando regla...", "deletingRule": "Eliminando regla...",
"enablingList": "Habilitando lista...", "enablingList": "Habilitando lista...",
"disablingList": "Deshabilitando lista...", "disablingList": "Deshabilitando lista...",
"savingList": "Guardando lista...",
"disableFiltering": "Deshabilitar filtrado", "disableFiltering": "Deshabilitar filtrado",
"enablingFiltering": "Habilitando filtrado...", "enablingFiltering": "Habilitando filtrado...",
"disablingFiltering": "Deshabilitando filtrado...", "disablingFiltering": "Deshabilitando filtrado...",
@ -649,7 +651,7 @@
"october": "Octubre", "october": "Octubre",
"november": "Noviembre", "november": "Noviembre",
"december": "Diciembre", "december": "Diciembre",
"malwarePhising": "Malware/phising", "malwarePhishing": "Malware/phising",
"queries": "Peticiones", "queries": "Peticiones",
"adultSites": "Sitios de adultos", "adultSites": "Sitios de adultos",
"quickFilters": "Filtros rápidos", "quickFilters": "Filtros rápidos",
@ -660,5 +662,69 @@
"topItemsOrderDescription": "Ordena las listas de top de elementos en la pantalla de inicio", "topItemsOrderDescription": "Ordena las listas de top de elementos en la pantalla de inicio",
"topItemsReorderInfo": "Mantén presionado y desliza un elemento para reordenarlo.", "topItemsReorderInfo": "Mantén presionado y desliza un elemento para reordenarlo.",
"discardChanges": "Descartar cambios", "discardChanges": "Descartar cambios",
"discardChangesDescription": "¿Estás seguro de que deseas descartar los cambios realizados?" "discardChangesDescription": "¿Estás seguro de que deseas descartar los cambios realizados?",
"others": "Otros",
"showChart": "Mostrar gráfico",
"hideChart": "Ocultar gráfico",
"showTopItemsChart": "Mostrar gráfico en top de items",
"showTopItemsChartDescription": "Muestra por defecto el gráfico de anillo en las secciones de top de items. Sólo afecta a la vista móvil.",
"openMenu": "Abrir menú",
"closeMenu": "Cerrar menú",
"openListUrl": "Abrir URL de lista",
"selectionMode": "Modo de selección",
"enableDisableSelected": "Activar o desactivar elementos seleccionados",
"deleteSelected": "Eliminar elementos seleccionados",
"deleteSelectedLists": "Eliminar listas seleccionadas",
"allSelectedListsDeletedSuccessfully": "Todas las listas seleccionadas han sido eliminadas correctamente.",
"deletionResult": "Resultado de eliminación",
"deletingLists": "Eliminando listas...",
"failedElements": "Elementos fallidos",
"processingLists": "Procesando listas...",
"enableDisableResult": "Resultado de activar o desactivar",
"selectedListsEnabledDisabledSuccessfully": "Todas las listas seleccionadas se han activado o desactivado correctamente.",
"sslWarning": "Si estás usando una conexión HTTPS con un certificado autofirmado, asegúrate de activar \"No comprobar el certificado SSL\" en Ajustes > Ajustes avanzados.",
"unsupportedServerVersion": "Versión del servidor no soportada",
"unsupportedServerVersionMessage": "La versión de tu servidor AdGuard Home es demasiado antigua y no está soportada por AdGuard Home Manager. Necesitarás actualizar tu servidor AdGuard Home a una versión más actual para utilizar esta aplicación.",
"yourVersion": "Tu versión: {version}",
"minimumRequiredVersion": "Versión mínima requerida: {version}",
"topUpstreams": "DNS de subida más frecuentes",
"averageUpstreamResponseTime": "Tiempo promedio de respuesta upstream",
"dhcpNotAvailable": "El servidor DHCP no está disponible.",
"osServerInstalledIncompatible": "El SO donde el servidor está instalado no es compatible con esta característica.",
"resetSettings": "Resetear configuración",
"resetEncryptionSettingsDescription": "Estás seguro que deseas restaurar a valores por defecto la configuración de encriptación?",
"resettingConfig": "Reseteando configuración...",
"configurationResetSuccessfully": "Configuración reseteada correctamente",
"configurationResetError": "La configuración no ha podido ser reseteada",
"testUpstreamDnsServers": "Probar servidores DNS de subida",
"errorTestUpstreamDns": "Error al probar los servidores DNS de subida.",
"useCustomIpEdns": "Usar IP personalizada para EDNS",
"useCustomIpEdnsDescription": "Permitir usar IP personalizada para EDNS",
"sortingOptions": "Opciones de ordenación",
"fromHighestToLowest": "De mayor a menor",
"fromLowestToHighest": "De menor a mayor",
"queryLogsAndStatistics": "Registro de consultas y estadísticas",
"ignoreClientQueryLog": "Ignorar este cliente en el registro de consultas",
"ignoreClientStatistics": "Ignorar este cliente en las estadísticas",
"savingChanges": "Guardando cambios...",
"fallbackDnsServers": "Servidores DNS alternativos",
"fallbackDnsServersDescription": "Configura los servidores DNS alternativos",
"fallbackDnsServersInfo": "Lista de servidores DNS alternativos utilizados cuando los servidores DNS de subida no responden. La sintaxis es la misma que en el campo de los principales DNS de subida anterior.",
"noFallbackDnsAdded": "No hay servidores DNS alternativos añadidos.",
"blockedResponseTtl": "Respuesta TTL bloqueada",
"blockedResponseTtlDescription": "Especifica durante cuántos segundos los clientes deben almacenar en cache una respuesta filtrada",
"invalidValue": "Valor no válido",
"noDataChart": "No hay datos para mostrar este gráfico.",
"noData": "No hay datos",
"unblockClient": "Desbloquear cliente",
"blockingClient": "Bloqueando cliente...",
"unblockingClient": "Desbloqueando cliente...",
"upstreamDnsCacheConfiguration": "Configuración de la caché DNS upstream",
"enableDnsCachingClient": "Habilitar caché de DNS para este cliente",
"dnsCacheSize": "Tamaño de caché de DNS",
"nameInvalid": "Se requiere un nombre",
"oneIdentifierRequired": "Se require al menos un identificador",
"dnsCacheNumber": "El tamaño de caché de DNS debe ser un número",
"errors": "Errores",
"redirectHttpsWarning": "Si tienes activado \"Redireccionar a HTTPS automáticamente\" en tu servidor AdGuard Home, debes seleccionar una conexión HTTPS y utilizar el puerto de HTTPS de tu servidor."
} }

View file

@ -170,7 +170,7 @@
"dnsQueries": "Zapytania DNS", "dnsQueries": "Zapytania DNS",
"average": "Średnia", "average": "Średnia",
"blockedFilters": "Zablokowane przez filtry", "blockedFilters": "Zablokowane przez filtry",
"malwarePhisingBlocked": "Zablokowane złośliwe oprogramowanie/phishing", "malwarePhishingBlocked": "Zablokowane złośliwe oprogramowanie/phishing",
"blockedAdultWebsites": "Zablokowane witryny dla dorosłych", "blockedAdultWebsites": "Zablokowane witryny dla dorosłych",
"generalSettings": "Ustawienia główne", "generalSettings": "Ustawienia główne",
"generalSettingsDescription": "Różne ustawienia", "generalSettingsDescription": "Różne ustawienia",
@ -649,7 +649,7 @@
"october": "Październik", "october": "Październik",
"november": "Listopad", "november": "Listopad",
"december": "Grudzień", "december": "Grudzień",
"malwarePhising": "Złośliwe oprogramowanie / wyłudzanie informacji", "malwarePhishing": "Złośliwe oprogramowanie / wyłudzanie informacji",
"queries": "Zapytania", "queries": "Zapytania",
"adultSites": "Strony dla dorosłych", "adultSites": "Strony dla dorosłych",
"quickFilters": "Szybkie filtry", "quickFilters": "Szybkie filtry",

692
lib/l10n/app_tr.arb Normal file
View file

@ -0,0 +1,692 @@
{
"home": "Anasayfa",
"settings": "Ayarlar",
"connect": "Bağlan",
"servers": "Sunucular",
"createConnection": "Bağlantı oluştur",
"editConnection": "Bağlantıyı düzenle",
"name": "Ad",
"ipDomain": "IP adresi veya alan adı",
"path": "Dosya Yolu",
"port": "Bağlantı noktası",
"username": "Kullanıcı adı",
"password": "Şifre",
"defaultServer": "Varsayılan sunucu",
"general": "Genel",
"connection": "Bağlantı",
"authentication": "Kimlik doğrulama",
"other": "Diğer",
"invalidPort": "Geçersiz bağlantı noktası",
"invalidPath": "Geçersiz dosya yolu",
"invalidIpDomain": "Geçersiz IP veya alan adı",
"ipDomainNotEmpty": "IP veya alan adı boş olamaz",
"nameNotEmpty": "Ad boş bırakılamaz",
"invalidUsernamePassword": "Geçersiz kullanıcı adı veya şifre",
"tooManyAttempts": "Çok fazla deneme yapıldı. Daha sonra tekrar deneyin.",
"cantReachServer": "Sunucuya ulaşılamıyor. Bağlantınızı kontrol edin.",
"sslError": "SSL hatası. Ayarlar > Gelişmiş ayarlar bölümüne gidin ve SSL doğrulamasını geçersiz kıl seçeneğini etkinleştirin.",
"unknownError": "Bilinmeyen hata",
"connectionNotCreated": "Bağlantı kurulamadı",
"connecting": "Bağlanılıyor...",
"connected": "Bağlantı kuruldu",
"selectedDisconnected": "Seçildi ancak bağlantı kesildi",
"connectionDefaultSuccessfully": "Bağlantı başarıyla varsayılan olarak ayarlandı.",
"connectionDefaultFailed": "Bağlantı varsayılan olarak ayarlanamadı.",
"noSavedConnections": "Kaydedilmiş bağlantı yok",
"cannotConnect": "Sunucuya bağlanılamıyor",
"connectionRemoved": "Bağlantı başarıyla kaldırıldı",
"connectionCannotBeRemoved": "Bağlantı kaldırılamaz.",
"remove": "Kaldır",
"removeWarning": "Bu AdGuard Home sunucusuyla olan bağlantıyı kaldırmak istediğinizden emin misiniz?",
"cancel": "İptal",
"defaultConnection": "Varsayılan bağlantı",
"setDefault": "Varsayılan ayarla",
"edit": "Düzenle",
"delete": "Sil",
"save": "Kaydet",
"serverStatus": "Sunucu durumu",
"connectionNotUpdated": "Bağlantı Güncellenmedi",
"ruleFilteringWidget": "Kural filtreleme",
"safeBrowsingWidget": "Güvenli gezinti",
"parentalFilteringWidget": "Ebeveyn filtreleme",
"safeSearchWidget": "Güvenli arama",
"ruleFiltering": "Kural filtreleme",
"safeBrowsing": "Güvenli gezinti",
"parentalFiltering": "Ebeveyn filtreleme",
"safeSearch": "Güvenli arama",
"serverStatusNotRefreshed": "Sunucu durumu yenilenemedi",
"loadingStatus": "Durum yükleniyor...",
"errorLoadServerStatus": "Sunucu durumu yüklenemedi",
"topQueriedDomains": "En çok sorgulananlar",
"viewMore": "Daha fazla göster",
"topClients": "Öne çıkan istemciler",
"topBlockedDomains": "En çok engellenenler",
"appSettings": "Uygulama ayarları",
"theme": "Tema",
"light": "Aydınlık",
"dark": "Karanlık",
"systemDefined": "Sistemle uyumlu hale getir",
"close": "Kapat",
"connectedTo": "Bağlandı:",
"selectedServer": "Seçili sunucu:",
"noServerSelected": "Seçili sunucu yok",
"manageServer": "Sunucuyu yönet",
"allProtections": "Tüm korumalar",
"userNotEmpty": "Kullanıcı adı boş bırakılamaz",
"passwordNotEmpty": "Şifre boş bırakılamaz",
"examplePath": "Örnek: /adguard",
"helperPath": "Ters proxy kullanıyorsanız",
"aboutApp": "Uygulama hakkında",
"appVersion": "Uygulama sürümü",
"createdBy": "Geliştirici",
"clients": "İstemciler",
"allowed": "İzin verildi",
"blocked": "Engellendi",
"noClientsList": "Bu listede hiç istemci yok",
"activeClients": "Etkin",
"removeClient": "İstemciyi kaldır",
"removeClientMessage": "Bu istemciyi listeden çıkarmak istediğinize emin misiniz?",
"confirm": "Onayla",
"removingClient": "İstemci kaldırılıyor...",
"clientNotRemoved": "İstemci listeden çıkarılamadı",
"addClient": "İstemci ekle",
"list": "Liste",
"ipAddress": "IP adresi",
"ipNotValid": "IP adresi geçersiz",
"clientAddedSuccessfully": "İstemci listeye başarıyla eklendi",
"addingClient": "İstemci ekleniyor...",
"clientNotAdded": "İstemci listeye eklenemedi",
"clientAnotherList": "Bu istemci henüz başka bir listede",
"noSavedLogs": "Kayıtlı günlük yok",
"logs": "Günlükler",
"copyLogsClipboard": "Günlükleri panoya kopyala",
"logsCopiedClipboard": "Günlükler panoya kopyalandı",
"advancedSettings": "Gelişmiş ayarlar",
"dontCheckCertificate": "SSL sertifikasını asla kontrol etme",
"dontCheckCertificateDescription": "Sunucunun SSL sertifikası doğrulamasını geçersiz kılar",
"advancedSetupDescription": "Gelişmiş seçenekleri yönet",
"settingsUpdatedSuccessfully": "Ayarlar başarıyla güncellendi.",
"cannotUpdateSettings": "Ayarlar güncellenemiyor.",
"restartAppTakeEffect": "Uygulamayı yeniden başlat",
"loadingLogs": "Günlükler yükleniyor...",
"logsNotLoaded": "Günlüklerin listesi yüklenemedi",
"processed": "İşlendi\nListe yok",
"processedRow": "İşlendi (Liste yok)",
"blockedBlacklist": "Engellendi\nKara Liste",
"blockedBlacklistRow": "Engellendi (Kara liste)",
"blockedSafeBrowsing": "Engellendi\nGüvenli gezinti",
"blockedSafeBrowsingRow": "Engellendi (Güvenli gezinti)",
"blockedParental": "Engellendi\nEbeveyn filtreleme",
"blockedParentalRow": "Engellendi (Ebeveyn filtreleme)",
"blockedInvalid": "Engellendi\nGeçersiz",
"blockedInvalidRow": "Engellendi (Geçersiz)",
"blockedSafeSearch": "Engellendi\nGüvenli arama",
"blockedSafeSearchRow": "Engellendi (Güvenli arama)",
"blockedService": "Engellendi\nBelirlenen hizmet",
"blockedServiceRow": "Engellendi (Belirlenen hizmet)",
"processedWhitelist": "İşlendi\nBeyaz liste",
"processedWhitelistRow": "İşlendi (Beyaz liste)",
"processedError": "İşlendi\nHata",
"processedErrorRow": "İşlendi (Hata)",
"rewrite": "Yeniden Yaz",
"status": "Durum",
"result": "Sonuç",
"time": "Zaman",
"blocklist": "Engelleme Listesi",
"request": "İstek",
"domain": "Alan adı",
"type": "Tip",
"clas": "Sınıf",
"response": "Yanıt",
"dnsServer": "DNS sunucusu",
"elapsedTime": "İşlem süresi",
"responseCode": "Yanıt kodu",
"client": "İstemci",
"deviceIp": "IP adresi",
"deviceName": "İstemci adı",
"logDetails": "Günlük detayları",
"blockingRule": "Engelleme kuralı",
"blockDomain": "Alan adını engelle",
"couldntGetFilteringStatus": "Filtreleme durumu alınamıyor",
"unblockDomain": "Alan adı engelini kaldır",
"userFilteringRulesNotUpdated": "Kullanıcı filtreleme kuralları güncellenemedi",
"userFilteringRulesUpdated": "Kullanıcı filtreleme kuralları başarıyla güncellendi",
"savingUserFilters": "Kullanıcı filtreleri kaydediliyor...",
"filters": "Filtreler",
"logsOlderThan": "Daha eski günlükler",
"responseStatus": "Yanıt durumu",
"selectTime": "Zaman seç",
"notSelected": "Seçili değil",
"resetFilters": "Filtreleri sıfırla",
"noLogsDisplay": "Gösterilecek günlük yok",
"noLogsThatOld": "Seçilen zaman için kaydedilmiş herhangi bir günlük bulunmuyor olabilir. Daha yakın bir zaman seçmeyi deneyin.",
"apply": "Uygula",
"selectAll": "Hepsini seç",
"unselectAll": "Seçimleri kaldır",
"all": "Hepsi",
"filtered": "Filtrelenmiş",
"checkAppLogs": "Uygulama günlüklerini kontrol edin",
"refresh": "Yenile",
"search": "Ara",
"dnsQueries": "DNS sorguları",
"average": "Ortalama",
"blockedFilters": "Engellenen alan adları",
"malwarePhishingBlocked": "Engellenen zararlı içerikler",
"blockedAdultWebsites": "Engellenen yetişkin içerikler",
"generalSettings": "Genel ayarlar",
"generalSettingsDescription": "Çeşitli farklı ayarları yönet",
"hideZeroValues": "Sıfır değerlerini gizle",
"hideZeroValuesDescription": "Ana ekranda, değeri sıfır olan blokları gizler.",
"webAdminPanel": "Web yönetim paneli",
"visitGooglePlay": "Google Play sayfasını ziyaret et",
"gitHub": "Kaynak kodlarına GitHub'dan ulaşabilirsiniz",
"blockClient": "İstemciyi engelle",
"selectTags": "Etiketleri seçin",
"noTagsSelected": "Seçili etiket yok",
"tags": "Etiketler",
"identifiers": "Tanımlayıcılar",
"identifier": "Tanımlayıcı",
"identifierHelper": "IP adresi, CIDR, MAC adresi veya ClientID",
"noIdentifiers": "Tanımlayıcı eklenmedi",
"useGlobalSettings": "Küresel ayarları kullan",
"enableFiltering": "Filtrelemeyi etkinleştir",
"enableSafeBrowsing": "Güvenli gezintiyi etkinleştir",
"enableParentalControl": "Ebeveyn kontrolünü etkinleştir",
"enableSafeSearch": "Güvenli aramayı aktif et",
"blockedServices": "Engellenen hizmetler",
"selectBlockedServices": "Engellenen hizmetleri seç",
"noBlockedServicesSelected": "Engellenen hizmetler seçilmedi",
"services": "Hizmetler",
"servicesBlocked": "Hizmetler engellendi",
"tagsSelected": "Seçilen etiketler",
"upstreamServers": "Üst kaynak sunucuları",
"serverAddress": "Sunucu adresi",
"noUpstreamServers": "Üst kaynak sunucusu yok.",
"willBeUsedGeneralServers": "Genel üst kaynak sunucuları kullanılacak.",
"added": "Eklenenler",
"clientUpdatedSuccessfully": "İstemci başarıyla güncellendi",
"clientNotUpdated": "İstemci güncellenemedi",
"clientDeletedSuccessfully": "İstemci başarıyla kaldırıldı",
"clientNotDeleted": "İstemci silinemedi",
"options": "Seçenekler",
"loadingFilters": "Filtreler yükleniyor...",
"filtersNotLoaded": "Filtreler yüklenemedi.",
"whitelists": "Beyaz listeler",
"blacklists": "Kara listeler",
"rules": "Kurallar",
"customRules": "Özel kurallar",
"enabledRules": "Etkin kurallar",
"enabled": "Etkin",
"disabled": "Devre dışı",
"rule": "Kural",
"addCustomRule": "Özel kural ekle",
"removeCustomRule": "Özel kuralı kaldır",
"removeCustomRuleMessage": "Bu özel kuralı kaldırmak istediğinizden emin misiniz?",
"updatingRules": "Özel kurallar güncelleniyor...",
"ruleRemovedSuccessfully": "Kural başarıyla kaldırıldı",
"ruleNotRemoved": "Kural kaldırılamadı",
"ruleAddedSuccessfully": "Kural başarıyla eklendi",
"ruleNotAdded": "Kural eklenemedi",
"noCustomFilters": "Özel filtreler yok",
"noBlockedClients": "Engellenmiş istemci yok",
"noBlackLists": "Kara listeler yok",
"noWhiteLists": "Beyaz listeler yok",
"addWhitelist": "Beyaz liste ekle",
"addBlacklist": "Kara liste ekle",
"urlNotValid": "Bağlantı adresi geçerli değil",
"urlAbsolutePath": "Bağlantı adresi veya kesin dosya yolu",
"addingList": "Liste ekleniyor...",
"listAdded": "Liste başarıyla eklendi. Eklenen öğeler:",
"listAlreadyAdded": "Liste zaten eklenmiş",
"listUrlInvalid": "Liste bağlantı adresi geçersiz",
"listNotAdded": "Liste eklenemedi",
"listDetails": "Liste detayları",
"listType": "Liste türü",
"whitelist": "Beyaz liste",
"blacklist": "Kara liste",
"latestUpdate": "Son güncelleme",
"disable": "Devre dışı bırak",
"enable": "Etkinleştir",
"currentStatus": "Mevcut durum",
"listDataUpdated": "Liste verileri başarıyla güncellendi",
"listDataNotUpdated": "Liste verileri güncellenemedi",
"updatingListData": "Liste verileri güncelleniyor...",
"editWhitelist": "Beyaz listeyi düzenle",
"editBlacklist": "Kara listeyi düzenle",
"deletingList": "Liste siliniyor...",
"listDeleted": "Liste başarıyla silindi",
"listNotDeleted": "Liste silinemedi",
"deleteList": "Listeyi sil",
"deleteListMessage": "Bu listeyi silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.",
"serverSettings": "Sunucu ayarları",
"serverInformation": "Sunucu bilgisi",
"serverInformationDescription": "Sunucu bilgisi ve durumunu öğren",
"loadingServerInfo": "Sunucu bilgisi yükleniyor...",
"serverInfoNotLoaded": "Sunucu bilgisi yüklenemedi.",
"dnsAddresses": "DNS adresleri",
"seeDnsAddresses": "DNS adreslerine göz at",
"dnsPort": "DNS bağlantı noktası",
"httpPort": "HTTP bağlantı noktası",
"protectionEnabled": "Koruma etkin mi?",
"dhcpAvailable": "DHCP mevcut mu?",
"serverRunning": "Sunucu çalışıyor mu?",
"serverVersion": "Sunucu sürümü",
"serverLanguage": "Sunucu dili",
"yes": "Evet",
"no": "Hayır",
"allowedClients": "İzin verilen istemciler",
"disallowedClients": "İzin verilmeyen istemciler",
"disallowedDomains": "İzin verilmeyen alan adları",
"accessSettings": "Erişim ayarları",
"accessSettingsDescription": "Sunucu için erişim kurallarını yapılandır",
"loadingClients": "İstemciler yükleniyor...",
"clientsNotLoaded": "İstemciler yüklenemedi.",
"noAllowedClients": "İzin verilmiş istemci yok",
"allowedClientsDescription": "Eğer bu liste girdiler içeriyorsa, AdGuard Home yalnızca bu istemcilerden gelen talepleri kabul edecektir.",
"blockedClientsDescription": "Bu liste girdileri içeriyorsa, AdGuard Home bu istemcilerden gelen talepleri reddedecektir. Bu alan adı, İzin Verilen İstemciler'de girdi varsa görmezden gelinir.",
"disallowedDomainsDescription": "AdGuard Home, bu alan adlarına uyan DNS sorgularını reddeder ve bu sorgular sorgu günlüğünde bile görünmez.",
"addClientFieldDescription": "CIDR'ler, IP adresi veya ClientID",
"clientIdentifier": "İstemci tanımlayıcısı",
"allowClient": "İstemciye izin ver",
"disallowClient": "İstemciye izin verme",
"noDisallowedDomains": "İzin verilmeyen alan adı yok",
"domainNotAdded": "Alan adı eklenemedi",
"statusSelected": "Durum seçildi.",
"updateLists": "Listeleri güncelle",
"checkHostFiltered": "Filtrelemeyi kontrol et",
"updatingLists": "Listeler güncelleniyor...",
"listsUpdated": "Listeler güncellendi",
"listsNotUpdated": "Listeler güncellenemedi",
"listsNotLoaded": "Listeler yüklenemedi",
"domainNotValid": "Alan adı geçersiz",
"check": "Kontrol et",
"checkingHost": "Kontrol ediliyor",
"errorCheckingHost": "Kontrol etme başarısız",
"block": "Engelle",
"unblock": "Engeli kaldır",
"custom": "Özel",
"addImportant": "Ekle ($important)",
"howCreateRules": "Özel kurallar nasıl oluşturulur?",
"examples": "Örnekler",
"example1": "example.org ve tüm alt alan adlarına erişimi engeller.",
"example2": "example.org ve tüm alt alan adlarına erişimi engellemeyi kaldırır.",
"example3": "Yorum ekler.",
"example4": "Belirtilen düzenli ifadeye uyan alan adlarına erişimi engeller.",
"moreInformation": "Daha fazla bilgi",
"addingRule": "Kural ekleniyor...",
"deletingRule": "Kural siliniyor...",
"enablingList": "Liste etkinleştiriliyor...",
"disablingList": "Liste devre dışı bırakılıyor...",
"savingList": "Liste kaydediliyor...",
"disableFiltering": "Filtrelemeyi devre dışı bırak",
"enablingFiltering": "Filtreleme etkinleştiriliyor...",
"disablingFiltering": "Filtreleme devre dışı bırakılıyor...",
"filteringStatusUpdated": "Filtreleme durumu başarıyla güncellendi",
"filteringStatusNotUpdated": "Filtreleme durumu güncellenemedi",
"updateFrequency": "Güncelleme sıklığı",
"never": "Asla",
"hour1": "1 saat",
"hours12": "12 saat",
"hours24": "24 saat",
"days3": "3 gün",
"days7": "7 gün",
"changingUpdateFrequency": "Değiştiriliyor...",
"updateFrequencyChanged": "Güncelleme sıklığı başarıyla değiştirildi",
"updateFrequencyNotChanged": "Güncelleme sıklığı değiştirilemedi",
"updating": "Değerler güncelleniyor...",
"blockedServicesUpdated": "Engellenen hizmetler başarıyla güncellendi",
"blockedServicesNotUpdated": "Engellenen hizmetler güncellenemedi",
"insertDomain": "Filtreleme durumunu kontrol etmek için bir alan adı ekleyin.",
"dhcpSettings": "DHCP ayarları",
"dhcpSettingsDescription": "DHCP sunucusunu yapılandır",
"dhcpSettingsNotLoaded": "DHCP ayarları yüklenemedi",
"loadingDhcp": "DHCP ayarları yükleniyor...",
"enableDhcpServer": "DHCP sunucusunu etkinleştir",
"selectInterface": "Arayüz seçin",
"hardwareAddress": "Donanım adresi",
"gatewayIp": "Ağ Geçidi IP'si",
"ipv4addresses": "IPv4 adresleri",
"ipv6addresses": "IPv6 adresleri",
"neededSelectInterface": "DHCP sunucusunu yapılandırmak için bir arayüz seçmeniz gerekir.",
"ipv4settings": "IPv4 ayarları",
"startOfRange": "Menzilin başlangıcı",
"endOfRange": "Menzilin sonu",
"ipv6settings": "IPv6 ayarları",
"subnetMask": "Alt ağ maskesi",
"subnetMaskNotValid": "Alt ağ maskesi geçerli değil",
"gateway": "Ağ Geçidi",
"gatewayNotValid": "Ağ geçidi geçerli değil",
"leaseTime": "Kira süresi",
"seconds": "{time} saniye",
"leaseTimeNotValid": "Kira süresi geçerli değil",
"restoreConfiguration": "Yapılandırmayı sıfırla",
"restoreConfigurationMessage": "Devam etmek istediğinizden emin misiniz? Bu, tüm yapılandırmayı sıfırlayacak. Bu işlem geri alınamaz.",
"changeInterface": "Arayüzü değiştir",
"savingSettings": "Ayarlar kaydediliyor...",
"settingsSaved": "Ayarlar başarıyla kaydedildi",
"settingsNotSaved": "Ayarlar kaydedilemedi",
"restoringConfig": "Yapılandırma geri yükleniyor...",
"configRestored": "Yapılandırma başarıyla sıfırlandı",
"configNotRestored": "Yapılandırma sıfırlanamadı",
"dhcpStatic": "DHCP statik kiralamaları",
"noDhcpStaticLeases": "DHCP statik kiralamaları bulunamadı",
"deleting": "Siliniyor...",
"staticLeaseDeleted": "DHCP statik kiralama başarıyla silindi",
"staticLeaseNotDeleted": "DHCP statik kiralaması silinemedi",
"deleteStaticLease": "Statik kiralamayı sil",
"deleteStaticLeaseDescription": "DHCP statik kirası silinecek. Bu işlem geri alınamaz.",
"addStaticLease": "Statik kiralama ekleyin",
"macAddress": "MAC adresi",
"macAddressNotValid": "MAC adresi geçersiz",
"hostName": "Ana bilgisayar adı",
"hostNameError": "Ana bilgisayar adı boş olamaz",
"creating": "Oluşturuluyor...",
"staticLeaseCreated": "DHCP statik kiralaması başarıyla oluşturuldu",
"staticLeaseNotCreated": "DHCP statik kiralaması oluşturulamadı",
"staticLeaseExists": "DHCP statik kiralaması zaten mevcut",
"serverNotConfigured": "Sunucu yapılandırılmamış",
"restoreLeases": "Kiralamaları sıfırla",
"restoreLeasesMessage": "Devam etmek istediğinizden emin misiniz? Bu, mevcut tüm kiralamaları sıfırlayacaktır. Bu işlem geri alınamaz.",
"restoringLeases": "Kiralamalar sıfırlanıyor...",
"leasesRestored": "Kiralamalar başarıyla sıfırlandı",
"leasesNotRestored": "Kiralar sıfırlanamadı",
"dhcpLeases": "DHCP kiralamaları",
"noLeases": "Kullanılabilir DHCP kiralaması yok",
"dnsRewrites": "DNS yeniden yazımları",
"dnsRewritesDescription": "Özel DNS kurallarını yapılandır",
"loadingRewriteRules": "Yeniden yazım kuralları yükleniyor...",
"rewriteRulesNotLoaded": "DNS yeniden yazım kuralları yüklenemedi.",
"noRewriteRules": "DNS yeniden yazım kuralları yok",
"answer": "Yanıt",
"deleteDnsRewrite": "DNS yeniden yazımı sil",
"deleteDnsRewriteMessage": "Bu DNS yeniden yazımını silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.",
"dnsRewriteRuleDeleted": "DNS yeniden yazım kuralı başarıyla silindi",
"dnsRewriteRuleNotDeleted": "DNS yeniden yazım kuralı silinemedi",
"addDnsRewrite": "DNS yeniden yazımı ekle",
"addingRewrite": "Yeniden yazım ekleniyor...",
"dnsRewriteRuleAdded": "DNS yeniden yazım kuralı başarıyla eklendi",
"dnsRewriteRuleNotAdded": "DNS yeniden yazım kuralı eklenemedi",
"logsSettings": "Günlük ayarları",
"enableLog": "Günlüğü etkinleştir",
"clearLogs": "Günlükleri temizle",
"anonymizeClientIp": "İstemci IP'sini anonimleştir",
"hours6": "6 saat",
"days30": "30 gün",
"days90": "90 gün",
"retentionTime": "Saklama süresi",
"selectOneItem": "Bir öğe seçin",
"logSettingsNotLoaded": "Günlük ayarları yüklenemedi.",
"updatingSettings": "Ayarlar güncelleniyor...",
"logsConfigUpdated": "Günlük ayarları başarıyla güncellendi",
"logsConfigNotUpdated": "Günlük ayarları başarıyla güncellendi",
"deletingLogs": "Günlükler temizleniyor...",
"logsCleared": "Günlükler başarıyla temizlendi",
"logsNotCleared": "Günlükler temizlenemedi",
"runningHomeAssistant": "Ev asistanı üzerinde çalıştır",
"serverError": "Sunucu hatası",
"noItems": "Burada gösterilecek öğe yok",
"dnsSettings": "DNS ayarları",
"dnsSettingsDescription": "DNS sunucuları ile bağlantıyı yapılandır",
"upstreamDns": "Üst kaynak DNS sunucuları",
"bootstrapDns": "Önyükleme DNS sunucuları",
"noUpstreamDns": "Üst kaynak DNS sunucuları eklenmedi.",
"dnsMode": "DNS modu",
"noDnsMode": "DNS modu seçili değil",
"loadBalancing": "Yük dengeleme",
"parallelRequests": "Paralel istekler",
"fastestIpAddress": "En hızlı IP adresi",
"loadBalancingDescription": "Her seferinde bir üst kaynak sunucusuna sorgu yap. AdGuard Home, sunucuyu seçmek için ağırlıklı rastgele algoritmasını kullanır, böylece en hızlı sunucu daha sık kullanılır.",
"parallelRequestsDescription": "Tüm üst kaynak sunucularını aynı anda sorgulayarak çözümlemeyi hızlandırmak için paralel sorgular kullanın.",
"fastestIpAddressDescription": "Tüm DNS sunucularına sorgu yapın ve tüm yanıtlar arasında en hızlı IP adresini döndürün. Bu, AdGuard Home'un tüm DNS sunucularından yanıtları beklemesi gerektiği için DNS sorgularını yavaşlatır, ancak genel bağlantıyı iyileştirir.",
"noBootstrapDns": "Önyükleme DNS sunucuları eklenmedi.",
"bootstrapDnsServersInfo": "Önyükleme DNS sunucuları, üst kaynaklarda belirttiğiniz DoH/DoT çözümleyicilerinin IP adreslerini çözmek için kullanılır.",
"privateReverseDnsServers": "Özel ters DNS sunucuları",
"privateReverseDnsServersDescription": "AdGuard Home'un yerel PTR sorguları için kullandığı DNS sunucuları. Bu sunucular, özel IP aralıklarındaki adresler için ters DNS kullanarak PTR isteklerini çözmek için kullanılır, örneğin '192.168.12.34' olarak ayarlanmamışsa AdGuard Home, AdGuard Home'un kendi adresleri dışında, işletim sisteminizin varsayılan DNS çözümleyicilerinin adreslerini kullanır.",
"reverseDnsDefault": "Varsayılan olarak, AdGuard Home aşağıdaki ters DNS çözümleyicilerini kullanır",
"addItem": "Öğe ekle",
"noServerAddressesAdded": "Sunucu adresleri eklenmedi.",
"usePrivateReverseDnsResolvers": "Özel ters DNS çözümleyicilerini kullan",
"usePrivateReverseDnsResolversDescription": "Bu üst kaynak sunucularını kullanarak yerel olarak sunulan adresler için ters DNS sorguları gerçekleştirin. Devre dışı bırakılırsa, AdGuard Home, DHCP, /etc/hosts vb. kaynaklardan bilinen istemciler dışında tüm PTR isteklerine NXDOMAIN yanıtı verir.",
"enableReverseResolving": "İstemcilerin IP adreslerinin ters çözümlemesini etkinleştir",
"enableReverseResolvingDescription": "İstemcilerin IP adreslerini karşılık gelen çözücülere PTR sorguları göndererek IP adreslerini tersine çözümleyerek (yerel istemciler için özel DNS sunucuları, genel IP adresine sahip istemciler için üst kaynak sunucuları) istemcilerin ana bilgisayar adlarını tersine çöz.",
"dnsServerSettings": "AdGuard Home DNS sunucusu ayarları",
"limitRequestsSecond": "Saniye başına sınırlama isteği",
"valueNotNumber": "Değer bir sayı değil",
"enableEdns": "EDNS istemci alt ağını etkinleştir",
"enableEdnsDescription": "Kaynak yönü isteklerine EDNS İstemci Alt Ağı Seçeneği (ECS) ekleyin ve istemciler tarafından gönderilen değerleri sorgu günlüğüne kaydedin.",
"enableDnssec": "DNSSEC'i etkinleştir",
"enableDnssecDescription": "Giden DNS sorguları için DNSSEC özelliğini etkinleştir ve sonucu kontrol edin.(DNSSEC etkinleştirilmiş bir çözümleyici gerekli)",
"disableResolvingIpv6": "IPv6 adreslerinin çözümlenmesini devre dışı bırak",
"disableResolvingIpv6Description": "IPv6 adresleri için tüm DNS sorgularını bırakın (AAAA yazın) ve HTTPS yanıtlarından IPv6 ipuçlarını kaldırın.",
"blockingMode": "Engelleme modu",
"defaultMode": "Varsayılan",
"defaultDescription": "Reklam engelleme tarzı bir kural tarafından engellendiğinde sıfır IP adresi ile yanıt verin. (A için 0.0.0.0; :: AAAA için) /etc/hosts tarzı bir kural tarafından engellendiğinde kuralda belirtilen IP adresi ile yanıt verin.",
"refusedDescription": "REFUSED kodu ile yanıt verin.",
"nxdomainDescription": "NXDOMAIN kodu ile yanıt verin.",
"nullIp": "Boş IP",
"nullIpDescription": "Sıfır IP adresi ile yanıt verin. (A için 0.0.0.0; :: AAAA için)",
"customIp": "Özel IP",
"customIpDescription": "Manuel olarak ayarlanmış bir IP adresi ile yanıt verin.",
"dnsCacheConfig": "DNS önbellek yapılandırması",
"cacheSize": "Önbellek boyutu",
"inBytes": "Bayt olarak",
"overrideMinimumTtl": "Minimum kullanım süresini geçersiz kıl",
"overrideMinimumTtlDescription": "DNS yanıtlarını önbelleğe alırken üst sunucudan alınan minimum kullanım süresi değerini ayarlayın (saniye olarak)",
"overrideMaximumTtl": "Maksimum kullanım süresini geçersiz kıl",
"overrideMaximumTtlDescription": "DNS önbelleğindeki girişler için maksimum kullanım süresi değerini ayarlayın (saniye olarak)",
"optimisticCaching": "İyimser önbelleğe alma",
"optimisticCachingDescription": "Girişlerin süresi dolmuş olsa bile Adguard Home'un önbellekten yanıt vermesini sağlayın ve aynı zamanda bunları yenilemeye çalışın.",
"loadingDnsConfig": "DNS yapılandırması yükleniyor...",
"dnsConfigNotLoaded": "DNS yapılandırması yüklenemedi.",
"blockingIpv4": "IPv4 engelleniyor",
"blockingIpv4Description": "Engellenen bir A isteği için döndürülecek IP adresi",
"blockingIpv6": "IPv6 engelleniyor",
"blockingIpv6Description": "Engellenen bir AAAA isteği için döndürülecek IP adresi",
"invalidIp": "Geçersiz IP adresi",
"dnsConfigSaved": "DNS sunucusu yapılandırması başarıyla kaydedildi",
"dnsConfigNotSaved": "DNS sunucusu yapılandırması kaydedilemedi",
"savingConfig": "Yapılandırma kaydediliyor...",
"someValueNotValid": "Bazı değerler geçerli değil",
"upstreamDnsDescription": "Üst kaynak sunucularını ve DNS modunu yapılandır",
"bootstrapDnsDescription": "Önyükleme DNS sunucularını yapılandır",
"privateReverseDnsDescription": "Özel DNS çözümleyicileri yapılandır ve özel ters DNS çözümlemeyi etkinleştir",
"dnsServerSettingsDescription": "Hız limiti, engelleme modu ve daha fazlasını yapılandır",
"dnsCacheConfigDescription": "Sunucunun DNS önbelleğini nasıl yöneteceğini yapılandır",
"comment": "Yorum",
"address": "Adres",
"commentsDescription": "Yorumlar her zaman # işareti ile başlar. Onu eklemenize gerek yok, otomatik olarak eklenir.",
"encryptionSettings": "Şifreleme ayarları",
"encryptionSettingsDescription": "Şifreleme (HTTPS/QUIC/TLS) desteği",
"loadingEncryptionSettings": "Şifreleme ayarları yükleniyor...",
"encryptionSettingsNotLoaded": "Şifreleme ayarları yüklenemedi.",
"enableEncryption": "Şifrelemeyi etkinleştir",
"enableEncryptionTypes": "HTTPS, DNS-over-HTTPS ve DNS-over-TLS",
"enableEncryptionDescription": "Eğer şifreleme etkinleştirilmişse, AdGuard Home yönetici arayüzü HTTPS üzerinden çalışacaktır ve DNS sunucusu DNS üzerinden HTTPS ve TLS ile gelen isteklere yanıt verecektir.",
"serverConfiguration": "Sunucu yapılandırması",
"domainName": "Alan adı",
"domainNameDescription": "Eğer ayarlanırsa, AdGuard Home istemci kimliklerini tespit eder, DDR sorgularına yanıt verir ve ek bağlantı doğrulamalarını gerçekleştirir. Ayarlanmazsa, bu özellikler devre dışı bırakılır. Sertifikadaki DNS adlarından biriyle eşleşmelidir.",
"redirectHttps": "Otomatik olarak HTTPS'e yönlendir",
"httpsPort": "HTTPS bağlantı noktası",
"tlsPort": "DNS-over-TLS bağlantı noktası",
"dnsOverQuicPort": "DNS-over-QUIC bağlantı noktası",
"certificates": "Sertifikalar",
"certificatesDescription": "Şifreleme kullanmak için, alan adınız için geçerli bir SSL sertifikası zinciri sağlamanız gereklidir. letsencrypt.org'dan ücretsiz bir sertifika alabilir veya güvenilir sertifika yetkililerinden satın alabilirsiniz.",
"certificateFilePath": "Sertifika dosyası belirle",
"pasteCertificateContent": "Sertifika içeriğini yapıştır",
"certificatePath": "Sertifika dosya yolu",
"certificateContent": "Sertifika içeriği",
"privateKey": "Özel anahtarlar",
"privateKeyFile": "Özel anahtar dosyası belirle",
"pastePrivateKey": "Özel anahtar içeriğini yapıştır",
"usePreviousKey": "Önceden kaydedilmiş anahtarı kullan",
"privateKeyPath": "Özel anahtar dosya yolu",
"invalidCertificate": "Geçersiz sertifika",
"invalidPrivateKey": "Geçersiz özel anahtar",
"validatingData": "Veri doğrulama",
"dataValid": "Veri geçerli",
"dataNotValid": "Veri geçersiz",
"encryptionConfigSaved": "Şifreleme yapılandırması başarıyla kaydedildi",
"encryptionConfigNotSaved": "Şifreleme yapılandırması kaydedilemedi",
"configError": "Yapılandırma hatası",
"enterOnlyCertificate": "Yalnızca sertifikayı girin. ---BEGIN--- ve ---END--- satırlarını girmeyin.",
"enterOnlyPrivateKey": "Yalnızca anahtarı girin. ---BEGIN--- ve ---END--- satırlarını girmeyin.",
"noItemsSearch": "Bu arama için hiçbir öğe yok.",
"clearSearch": "Aramayı temizle",
"exitSearch": "Aramadan çık",
"searchClients": "İstemcileri ara",
"noClientsSearch": "Bu arama ile ilgili hiçbir istemci bulunamadı.",
"customization": "Özelleştirme",
"customizationDescription": "Bu uygulamayı özelleştir",
"color": "Renk",
"useDynamicTheme": "Dinamik renk teması kullan",
"red": "Kırmızı",
"green": "Yeşil",
"blue": "Mavi",
"yellow": "Sarı",
"orange": "Turuncu",
"brown": "Kahverengi",
"cyan": "Camgöbeği",
"purple": "Mor",
"pink": "Pembe",
"deepOrange": "Koyu turuncu",
"indigo": "Çivit mavisi",
"useThemeColorStatus": "Durum için tema rengini kullan",
"useThemeColorStatusDescription": "Yeşil ve kırmızı durum renklerini tema rengi ve gri ile değiştirir.",
"invalidCertificateChain": "Geçersiz sertifika zinciri",
"validCertificateChain": "Geçerli sertifika zinciri",
"subject": "Konu",
"issuer": "Veren",
"expires": "Süresi dolacak",
"validPrivateKey": "Geçerli özel anahtar",
"expirationDate": "Son kullanma tarihi",
"keysNotMatch": "Geçersiz bir sertifika veya anahtar: tls: özel anahtar genel anahtarla eşleşmiyor.",
"timeLogs": "Günlüklerdeki işlem süresi",
"timeLogsDescription": "Günlükler listesinde zaman yerine işlem süresini göster",
"hostNames": "Ana bilgisayar adları",
"keyType": "Anahtar türü",
"updateAvailable": "Güncelleme mevcut",
"installedVersion": "Yüklü sürüm",
"newVersion": "Yeni sürüm",
"source": "Kaynak",
"downloadUpdate": "Güncellemeyi indir",
"download": "İndir",
"doNotRememberAgainUpdate": "Bu sürüm için tekrar hatırlama.",
"downloadingUpdate": "İndiriliyor",
"completed": "Tamamlandı",
"permissionNotGranted": "İzin verilmedi",
"inputSearchTerm": "Bir arama terimi girin.",
"answers": "Yanıtlar",
"copyClipboard": "Panoya kopyala",
"domainCopiedClipboard": "Alan adı panoya kopyalandı",
"clearDnsCache": "DNS önbelleğini temizle",
"clearDnsCacheMessage": "DNS önbelleğini temizlemek istediğinizden emin misiniz?",
"dnsCacheCleared": "DNS önbelleği başarıyla temizlendi",
"clearingDnsCache": "Önbellek temizleniyor...",
"dnsCacheNotCleared": "DNS önbelleği temizlenemedi",
"clientsSelected": "Seçilmiş istemci",
"invalidDomain": "Geçersiz alan adı",
"loadingBlockedServicesList": "Engellenen hizmetler listesi yükleniyor...",
"blockedServicesListNotLoaded": "Engellenen hizmetler listesi yüklenemedi",
"error": "Hata",
"updates": "Güncellemeler",
"updatesDescription": "AdGuard Home sunucusunu güncelle",
"updateNow": "Şimdi güncelle",
"currentVersion": "Mevcut sürüm",
"requestStartUpdateFailed": "Güncellemeyi başlatma isteği başarısız oldu",
"requestStartUpdateSuccessful": "Güncellemeyi başlatma isteği başarılı",
"serverUpdated": "Sunucu güncellendi",
"unknownStatus": "Bilinmeyen durum",
"checkingUpdates": "Güncellemeler kontrol ediliyor...",
"checkUpdates": "Güncellemeleri kontrol et",
"requestingUpdate": "Güncelleme talep ediliyor...",
"autoupdateUnavailable": "Otomatik güncelleme kullanılamıyor",
"autoupdateUnavailableDescription": "Otomatik güncelleme servisi bu sunucu için kullanılamıyor. Bunun nedeni sunucunun bir Docker konteynerinde çalışıyor olması olabilir. Sunucunuzu manuel olarak güncellemeniz gerekecektir.",
"minute": "{time} dakika",
"minutes": "{time} dakika",
"hour": "{time} saat",
"hours": "{time} saat",
"remainingTime": "Kalan süre",
"safeSearchSettings": "Güvenli arama ayarları",
"loadingSafeSearchSettings": "Güvenli arama ayarları yükleniyor...",
"safeSearchSettingsNotLoaded": "Güvenli arama ayarları yüklenirken hata oluştu.",
"loadingLogsSettings": "Günlük ayarları yükleniyor...",
"selectOptionLeftColumn": "Sol sütundan bir seçenek seçin",
"selectClientLeftColumn": "Sol sütundan bir istemci seçin",
"disableList": "Listeyi devre dışı bırak",
"enableList": "Listeyi etkinleştir",
"screens": "Ekranlar",
"copiedClipboard": "Panoya kopyalandı",
"seeDetails": "Detayları gör",
"listNotAvailable": "Liste mevcut değil",
"copyListUrl": "Liste bağlantısını kopyala",
"listUrlCopied": "Panoya kopyalanan bağlantı adresini listeleyin",
"unsupportedVersion": "Desteklenmeyen sürüm",
"unsupprtedVersionMessage": "Sunucu sürümünüz {version} için destek garantisi verilmiyor. Bu uygulamanın bu sunucu sürümüyle çalışmasında bazı sorunlar olabilir. AdGuard Home Yöneticisi, AdGuard Home sunucunun kararlı sürümleriyle çalışacak şekilde tasarlanmıştır. Alfa ve beta sürümleriyle çalışabilir, ancak uyumluluk garanti edilmez ve uygulama bu sürümlerle çalışırken bazı sorunlar yaşayabilir.",
"iUnderstand": "Anladım",
"appUpdates": "Uygulama güncellemeleri",
"usingLatestVersion": "En son sürümü kullanıyorsunuz :)",
"ipLogs": "Günlüklerdeki IP",
"ipLogsDescription": "Günlükler listesinde istemci adı yerine IP adresini göster",
"application": "Uygulama",
"combinedChart": "Birleştirilmiş grafik",
"combinedChartDescription": "Tüm grafikleri bir araya getirir.",
"statistics": "İstatistikler",
"errorLoadFilters": "Filtreler yüklenirken hata oluştu.",
"clientRemovedSuccessfully": "İstemci başarıyla kaldırıldı.",
"editRewriteRule": "Yeniden yazım kuralını düzenle",
"dnsRewriteRuleUpdated": "DNS yeniden yazım kuralı başarıyla güncellendi",
"dnsRewriteRuleNotUpdated": "DNS yeniden yazım kuralı güncellenemedi",
"updatingRule": "Kural güncelleniyor...",
"serverUpdateNeeded": "Sunucu güncellemesi gerekli",
"updateYourServer": "Bu özelliği kullanmak için AdGuard Home sunucunuzu {version} veya üzeri bir sürüme güncelleyin.",
"january": "Ocak",
"february": "Şubat",
"march": "Mart",
"april": "Nisan",
"may": "Mayıs",
"june": "Haziran",
"july": "Temmuz",
"august": "Ağustos",
"september": "Eylül",
"october": "Ekim",
"november": "Kasım",
"december": "Aralık",
"malwarePhishing": "Zararlı yazılım/oltalama",
"queries": "Sorgular",
"adultSites": "Yetişkin içerikler",
"quickFilters": "Hızlı filtreler",
"searchDomainInternet": "İnternette alan adı ara",
"hideServerAddress": "Sunucu adresini gizle",
"hideServerAddressDescription": "Ana ekranda sunucu adresini gizler.",
"topItemsOrder": "Öne çıkan öğeler sıralaması",
"topItemsOrderDescription": "Ana ekrandaki öne çıkan öğe listelerini sırala",
"topItemsReorderInfo": "Yeniden sıralamak için bir öğeyi basılı tutun ve kaydırın.",
"discardChanges": "Değişiklikleri iptal et",
"discardChangesDescription": "Değişiklikleri iptal etmek istediğinizden emin misiniz?",
"others": "Diğerleri",
"showChart": "Göster",
"hideChart": "Gizle",
"showTopItemsChart": "Öne çıkan öğeler grafiği",
"showTopItemsChartDescription": "Varsayılan olarak öne çıkan öğeler bölümünde halka grafiğini gösterir. Sadece mobil görünümü etkiler.",
"openMenu": "Menüyü genişlet",
"closeMenu": "Menüyü daralt",
"openListUrl": "Liste bağlantısını aç",
"selectionMode": "Seçim modu",
"enableDisableSelected": "Seçili öğeleri etkinleştir veya devre dışı bırak",
"deleteSelected": "Seçili öğeleri sil",
"deleteSelectedLists": "Seçili listeleri sil",
"allSelectedListsDeletedSuccessfully": "Seçilen tüm listeler başarıyla silindi.",
"deletionResult": "Silinme sonucu",
"deletingLists": "Listeler siliniyor...",
"failedElements": "Başarısız öğeler",
"processingLists": "Listeler işleniyor...",
"enableDisableResult": "Sonucu etkinleştir veya devre dışı bırak",
"selectedListsEnabledDisabledSuccessfully": "Seçilen tüm listeler başarıyla etkinleştirildi veya devre dışı bırakıldı",
"sslWarning": "Kendinden imzalı bir sertifika ile HTTPS bağlantısı kullanıyorsanız, Ayarlar > Gelişmiş ayarlar bölümünde \"SSL sertifikasını asla kontrol etme\" seçeneğini etkinleştirdiğinizden emin olun.",
"unsupportedServerVersion": "Desteklenmeyen sunucu sürümü",
"unsupportedServerVersionMessage": "AdGuard Home sunucu sürümünüz çok eski ve AdGuard Home Manager tarafından desteklenmiyor. Bu uygulamayı kullanmak için AdGuard Home sunucunuzu daha yeni bir sürüme yükseltmeniz gerekecektir.",
"yourVersion": "Yüklü sürüm: {version}",
"minimumRequiredVersion": "Gerekli minimum sürüm: {version}",
"topUpstreams": "Öne çıkan üst kaynaklar",
"averageUpstreamResponseTime": "Üst kaynak ortalama yanıt süresi"
}

View file

@ -170,7 +170,7 @@
"dnsQueries": "DNS 查询", "dnsQueries": "DNS 查询",
"average": "平均值", "average": "平均值",
"blockedFilters": "被过滤器拦截", "blockedFilters": "被过滤器拦截",
"malwarePhisingBlocked": "被拦截的恶意/钓鱼网站", "malwarePhishingBlocked": "被拦截的恶意/钓鱼网站",
"blockedAdultWebsites": "被拦截的成人网站", "blockedAdultWebsites": "被拦截的成人网站",
"generalSettings": "常规设置", "generalSettings": "常规设置",
"generalSettingsDescription": "各种不同的设置", "generalSettingsDescription": "各种不同的设置",
@ -649,9 +649,16 @@
"october": "10月", "october": "10月",
"november": "11月", "november": "11月",
"december": "12月", "december": "12月",
"malwarePhising": "恶意/钓鱼网站", "malwarePhishing": "恶意/钓鱼网站",
"queries": "查询", "queries": "查询",
"adultSites": "成人网站", "adultSites": "成人网站",
"quickFilters": "状态过滤器", "quickFilters": "状态过滤器",
"searchDomainInternet": "在互联网上搜索该域名" "searchDomainInternet": "在互联网上搜索该域名",
"hideServerAddress": "隐藏服务器地址",
"hideServerAddressDescription": "在主页上隐藏服务器地址",
"topItemsOrder": "顶部项目顺序",
"topItemsOrderDescription": "排列主页顶部项目列表",
"topItemsReorderInfo": "按住并滑动一个项目以重新排序。",
"discardChanges": "放弃更改",
"discardChangesDescription": "您确定要放弃更改吗?"
} }

View file

@ -170,7 +170,7 @@
"dnsQueries": "DNS 查询", "dnsQueries": "DNS 查询",
"average": "平均值", "average": "平均值",
"blockedFilters": "被过滤器拦截", "blockedFilters": "被过滤器拦截",
"malwarePhisingBlocked": "被拦截的恶意/钓鱼网站", "malwarePhishingBlocked": "被拦截的恶意/钓鱼网站",
"blockedAdultWebsites": "被拦截的成人网站", "blockedAdultWebsites": "被拦截的成人网站",
"generalSettings": "常规设置", "generalSettings": "常规设置",
"generalSettingsDescription": "各种不同的设置", "generalSettingsDescription": "各种不同的设置",
@ -649,9 +649,16 @@
"october": "10月", "october": "10月",
"november": "11月", "november": "11月",
"december": "12月", "december": "12月",
"malwarePhising": "恶意/钓鱼网站", "malwarePhishing": "恶意/钓鱼网站",
"queries": "查询", "queries": "查询",
"adultSites": "成人网站", "adultSites": "成人网站",
"quickFilters": "状态过滤器", "quickFilters": "状态过滤器",
"searchDomainInternet": "在互联网上搜索该域名" "searchDomainInternet": "在互联网上搜索该域名",
"hideServerAddress": "隐藏服务器地址",
"hideServerAddressDescription": "在主页上隐藏服务器地址",
"topItemsOrder": "顶部项目顺序",
"topItemsOrderDescription": "排列主页顶部项目列表",
"topItemsReorderInfo": "按住并滑动一个项目以重新排序。",
"discardChanges": "放弃更改",
"discardChangesDescription": "您确定要放弃更改吗?"
} }

View file

@ -2,9 +2,10 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:flutter/services.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_dotenv/flutter_dotenv.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:flutter_displaymode/flutter_displaymode.dart';
import 'package:dynamic_color/dynamic_color.dart'; import 'package:dynamic_color/dynamic_color.dart';
import 'package:device_info_plus/device_info_plus.dart'; import 'package:device_info_plus/device_info_plus.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
@ -15,7 +16,8 @@ import 'package:window_size/window_size.dart';
import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/base.dart'; import 'package:adguard_home_manager/widgets/layout.dart';
import 'package:adguard_home_manager/widgets/menu_bar.dart';
import 'package:adguard_home_manager/providers/logs_provider.dart'; import 'package:adguard_home_manager/providers/logs_provider.dart';
import 'package:adguard_home_manager/providers/app_config_provider.dart'; import 'package:adguard_home_manager/providers/app_config_provider.dart';
@ -34,6 +36,7 @@ import 'package:adguard_home_manager/services/db/database.dart';
void main() async { void main() async {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) { if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
setWindowMinSize(const Size(500, 500)); setWindowMinSize(const Size(500, 500));
@ -196,6 +199,7 @@ class _MainState extends State<Main> {
@override @override
void initState() { void initState() {
displayMode(); displayMode();
super.initState(); super.initState();
} }
@ -230,22 +234,16 @@ class _MainState extends State<Main> {
Locale('zh', ''), Locale('zh', ''),
Locale('zh', 'CN'), Locale('zh', 'CN'),
Locale('pl', ''), Locale('pl', ''),
Locale('tr', ''),
Locale('ru', '') Locale('ru', '')
], ],
scaffoldMessengerKey: scaffoldMessengerKey, scaffoldMessengerKey: scaffoldMessengerKey,
builder: (context, child) { navigatorKey: globalNavigatorKey,
return MediaQuery( builder: (context, child) => CustomMenuBar(
data: MediaQuery.of(context).copyWith( child: child!,
textScaleFactor: !(Platform.isAndroid || Platform.isIOS) ),
? 0.9 home: const Layout(),
: 1.0
),
child: child!,
);
},
home: const Base(),
), ),
); );
} }
} }

View file

@ -4,14 +4,14 @@ class AppScreen {
final String name; final String name;
final IconData icon; final IconData icon;
final PreferredSizeWidget? appBar; final PreferredSizeWidget? appBar;
final Widget body;
final Widget? fab; final Widget? fab;
final Widget child;
const AppScreen({ const AppScreen({
required this.name, required this.name,
required this.icon, required this.icon,
this.appBar, this.appBar,
required this.body, this.fab,
this.fab required this.child,
}); });
} }

View file

@ -84,10 +84,13 @@ class Client {
final bool filteringEnabled; final bool filteringEnabled;
final bool parentalEnabled; final bool parentalEnabled;
final bool safebrowsingEnabled; final bool safebrowsingEnabled;
final bool? safesearchEnabled;
final bool useGlobalBlockedServices; final bool useGlobalBlockedServices;
final bool useGlobalSettings; final bool useGlobalSettings;
final SafeSearch? safeSearch; final SafeSearch? safeSearch;
final bool? ignoreQuerylog;
final bool? ignoreStatistics;
final bool? upstreamsCacheEnabled;
final int? upstreamsCacheSize;
Client({ Client({
required this.name, required this.name,
@ -98,10 +101,13 @@ class Client {
required this.filteringEnabled, required this.filteringEnabled,
required this.parentalEnabled, required this.parentalEnabled,
required this.safebrowsingEnabled, required this.safebrowsingEnabled,
required this.safesearchEnabled,
required this.useGlobalBlockedServices, required this.useGlobalBlockedServices,
required this.useGlobalSettings, required this.useGlobalSettings,
required this.safeSearch, required this.safeSearch,
required this.ignoreQuerylog,
required this.ignoreStatistics,
required this.upstreamsCacheEnabled,
required this.upstreamsCacheSize,
}); });
factory Client.fromJson(Map<String, dynamic> json) => Client( factory Client.fromJson(Map<String, dynamic> json) => Client(
@ -113,12 +119,15 @@ class Client {
filteringEnabled: json["filtering_enabled"], filteringEnabled: json["filtering_enabled"],
parentalEnabled: json["parental_enabled"], parentalEnabled: json["parental_enabled"],
safebrowsingEnabled: json["safebrowsing_enabled"], safebrowsingEnabled: json["safebrowsing_enabled"],
safesearchEnabled: json["safesearch_enabled"],
useGlobalBlockedServices: json["use_global_blocked_services"], useGlobalBlockedServices: json["use_global_blocked_services"],
useGlobalSettings: json["use_global_settings"], useGlobalSettings: json["use_global_settings"],
safeSearch: json["safe_search"] != null safeSearch: json["safe_search"] != null
? SafeSearch.fromJson(json["safe_search"]) ? SafeSearch.fromJson(json["safe_search"])
: null : null,
ignoreQuerylog: json["ignore_querylog"],
ignoreStatistics: json["ignore_statistics"],
upstreamsCacheEnabled: json["upstreams_cache_enabled"],
upstreamsCacheSize: json["upstreams_cache_size"]
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
@ -130,9 +139,12 @@ class Client {
"filtering_enabled": filteringEnabled, "filtering_enabled": filteringEnabled,
"parental_enabled": parentalEnabled, "parental_enabled": parentalEnabled,
"safebrowsing_enabled": safebrowsingEnabled, "safebrowsing_enabled": safebrowsingEnabled,
"safesearch_enabled": safesearchEnabled,
"safe_search": safeSearch, "safe_search": safeSearch,
"use_global_blocked_services": useGlobalBlockedServices, "use_global_blocked_services": useGlobalBlockedServices,
"use_global_settings": useGlobalSettings, "use_global_settings": useGlobalSettings,
"ignore_querylog": ignoreQuerylog,
"ignore_statistics": ignoreStatistics,
"upstreams_cache_enabled": upstreamsCacheEnabled,
"upstreams_cache_size": upstreamsCacheSize
}; };
} }

View file

@ -1,9 +1,11 @@
import 'dart:convert'; import 'dart:convert';
class DhcpModel { class DhcpModel {
bool dhcpAvailable;
List<NetworkInterface> networkInterfaces; List<NetworkInterface> networkInterfaces;
DhcpStatus dhcpStatus; DhcpStatus? dhcpStatus;
DhcpModel({ DhcpModel({
required this.dhcpAvailable,
required this.networkInterfaces, required this.networkInterfaces,
required this.dhcpStatus, required this.dhcpStatus,
}); });
@ -72,11 +74,11 @@ class DhcpStatus {
factory DhcpStatus.fromJson(Map<String, dynamic> json) => DhcpStatus( factory DhcpStatus.fromJson(Map<String, dynamic> json) => DhcpStatus(
interfaceName: json["interface_name"], interfaceName: json["interface_name"],
v4: IpVersion.fromJson(json["v4"]), v4: json["v4"] != null ? IpVersion.fromJson(json["v4"]) : null,
v6: IpVersion.fromJson(json["v6"]), v6: json["v6"] != null ? IpVersion.fromJson(json["v6"]) : null,
leases: List<Lease>.from(json["leases"].map((x) => Lease.fromJson(x))), leases: List<Lease>.from(json["leases"].map((x) => Lease.fromJson(x))),
staticLeases: List<Lease>.from(json["static_leases"].map((x) => Lease.fromJson(x))), staticLeases: List<Lease>.from(json["static_leases"].map((x) => Lease.fromJson(x))),
enabled: json["enabled"], enabled: json["enabled"] ?? false,
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {

View file

@ -2,32 +2,39 @@ class DnsInfo {
List<String> upstreamDns; List<String> upstreamDns;
String? upstreamDnsFile; String? upstreamDnsFile;
List<String> bootstrapDns; List<String> bootstrapDns;
List<String>? fallbackDns;
bool protectionEnabled; bool protectionEnabled;
int ratelimit; int ratelimit;
String blockingMode; String blockingMode;
bool ednsCsEnabled; bool ednsCsEnabled;
bool? ednsCsUseCustom;
String? ednsCsCustomIp;
bool dnssecEnabled; bool dnssecEnabled;
bool disableIpv6; bool disableIpv6;
String? upstreamMode; String? upstreamMode;
int? cacheSize; int? cacheSize;
int? cacheTtlMin; int? cacheTtlMin;
int? cacheTtlMax; int? cacheTtlMax;
bool cacheOptimistic; bool? cacheOptimistic;
bool resolveClients; bool? resolveClients;
bool usePrivatePtrResolvers; bool? usePrivatePtrResolvers;
List<String> localPtrUpstreams; List<String> localPtrUpstreams;
String blockingIpv4; String blockingIpv4;
String blockingIpv6; String blockingIpv6;
List<String> defaultLocalPtrUpstreams; List<String> defaultLocalPtrUpstreams;
int? blockedResponseTtl;
DnsInfo({ DnsInfo({
required this.upstreamDns, required this.upstreamDns,
required this.upstreamDnsFile, required this.upstreamDnsFile,
required this.bootstrapDns, required this.bootstrapDns,
required this.fallbackDns,
required this.protectionEnabled, required this.protectionEnabled,
required this.ratelimit, required this.ratelimit,
required this.blockingMode, required this.blockingMode,
required this.ednsCsEnabled, required this.ednsCsEnabled,
required this.ednsCsUseCustom,
required this.ednsCsCustomIp,
required this.dnssecEnabled, required this.dnssecEnabled,
required this.disableIpv6, required this.disableIpv6,
required this.upstreamMode, required this.upstreamMode,
@ -41,16 +48,20 @@ class DnsInfo {
required this.blockingIpv4, required this.blockingIpv4,
required this.blockingIpv6, required this.blockingIpv6,
required this.defaultLocalPtrUpstreams, required this.defaultLocalPtrUpstreams,
required this.blockedResponseTtl,
}); });
factory DnsInfo.fromJson(Map<String, dynamic> json) => DnsInfo( factory DnsInfo.fromJson(Map<String, dynamic> json) => DnsInfo(
upstreamDns: json["upstream_dns"] != null ? List<String>.from(json["upstream_dns"].map((x) => x)) : [], upstreamDns: json["upstream_dns"] != null ? List<String>.from(json["upstream_dns"].map((x) => x)) : [],
upstreamDnsFile: json["upstream_dns_file"], upstreamDnsFile: json["upstream_dns_file"],
bootstrapDns: json["bootstrap_dns"] != null ? List<String>.from(json["bootstrap_dns"].map((x) => x)) : [], bootstrapDns: json["bootstrap_dns"] != null ? List<String>.from(json["bootstrap_dns"].map((x) => x)) : [],
fallbackDns: json["fallback_dns"] != null ? List<String>.from(json["fallback_dns"].map((x) => x)) : [],
protectionEnabled: json["protection_enabled"], protectionEnabled: json["protection_enabled"],
ratelimit: json["ratelimit"], ratelimit: json["ratelimit"],
blockingMode: json["blocking_mode"], blockingMode: json["blocking_mode"],
ednsCsEnabled: json["edns_cs_enabled"], ednsCsEnabled: json["edns_cs_enabled"],
ednsCsUseCustom: json["edns_cs_use_custom"],
ednsCsCustomIp: json["edns_cs_custom_ip"],
dnssecEnabled: json["dnssec_enabled"], dnssecEnabled: json["dnssec_enabled"],
disableIpv6: json["disable_ipv6"], disableIpv6: json["disable_ipv6"],
upstreamMode: json["upstream_mode"], upstreamMode: json["upstream_mode"],
@ -64,16 +75,20 @@ class DnsInfo {
blockingIpv4: json["blocking_ipv4"], blockingIpv4: json["blocking_ipv4"],
blockingIpv6: json["blocking_ipv6"], blockingIpv6: json["blocking_ipv6"],
defaultLocalPtrUpstreams: json["default_local_ptr_upstreams"] != null ? List<String>.from(json["default_local_ptr_upstreams"].map((x) => x)) : [], defaultLocalPtrUpstreams: json["default_local_ptr_upstreams"] != null ? List<String>.from(json["default_local_ptr_upstreams"].map((x) => x)) : [],
blockedResponseTtl: json["blocked_response_ttl"]
); );
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
"upstream_dns": List<dynamic>.from(upstreamDns.map((x) => x)), "upstream_dns": List<dynamic>.from(upstreamDns.map((x) => x)),
"upstream_dns_file": upstreamDnsFile, "upstream_dns_file": upstreamDnsFile,
"bootstrap_dns": List<dynamic>.from(bootstrapDns.map((x) => x)), "bootstrap_dns": List<dynamic>.from(bootstrapDns.map((x) => x)),
"fallback_dns": List<dynamic>.from(bootstrapDns.map((x) => x)),
"protection_enabled": protectionEnabled, "protection_enabled": protectionEnabled,
"ratelimit": ratelimit, "ratelimit": ratelimit,
"blocking_mode": blockingMode, "blocking_mode": blockingMode,
"edns_cs_enabled": ednsCsEnabled, "edns_cs_enabled": ednsCsEnabled,
"edns_cs_use_custom": ednsCsUseCustom,
"edns_cs_custom_ip": ednsCsCustomIp,
"dnssec_enabled": dnssecEnabled, "dnssec_enabled": dnssecEnabled,
"disable_ipv6": disableIpv6, "disable_ipv6": disableIpv6,
"upstream_mode": upstreamMode, "upstream_mode": upstreamMode,
@ -87,5 +102,6 @@ class DnsInfo {
"blocking_ipv4": blockingIpv4, "blocking_ipv4": blockingIpv4,
"blocking_ipv6": blockingIpv6, "blocking_ipv6": blockingIpv6,
"default_local_ptr_upstreams": List<dynamic>.from(defaultLocalPtrUpstreams.map((x) => x)), "default_local_ptr_upstreams": List<dynamic>.from(defaultLocalPtrUpstreams.map((x) => x)),
"blocked_response_ttl": blockedResponseTtl
}; };
} }

View file

@ -9,6 +9,8 @@ class DnsStatistics {
final List<Map<String, int>> topQueriedDomains; final List<Map<String, int>> topQueriedDomains;
final List<Map<String, int>> topClients; final List<Map<String, int>> topClients;
final List<Map<String, int>> topBlockedDomains; final List<Map<String, int>> topBlockedDomains;
final List<Map<String, int>>? topUpstreamResponses;
final List<Map<String, double>>? topUpstreamsAvgTime;
final List<int> dnsQueries; final List<int> dnsQueries;
final List<int> blockedFiltering; final List<int> blockedFiltering;
final List<int> replacedSafebrowsing; final List<int> replacedSafebrowsing;
@ -25,6 +27,8 @@ class DnsStatistics {
required this.topQueriedDomains, required this.topQueriedDomains,
required this.topClients, required this.topClients,
required this.topBlockedDomains, required this.topBlockedDomains,
required this.topUpstreamResponses,
required this.topUpstreamsAvgTime,
required this.dnsQueries, required this.dnsQueries,
required this.blockedFiltering, required this.blockedFiltering,
required this.replacedSafebrowsing, required this.replacedSafebrowsing,
@ -42,6 +46,8 @@ class DnsStatistics {
topQueriedDomains: List<Map<String, int>>.from(json["top_queried_domains"].map((x) => Map.from(x).map((k, v) => MapEntry<String, int>(k, v)))), topQueriedDomains: List<Map<String, int>>.from(json["top_queried_domains"].map((x) => Map.from(x).map((k, v) => MapEntry<String, int>(k, v)))),
topClients: List<Map<String, int>>.from(json["top_clients"].map((x) => Map.from(x).map((k, v) => MapEntry<String, int>(k, v)))), topClients: List<Map<String, int>>.from(json["top_clients"].map((x) => Map.from(x).map((k, v) => MapEntry<String, int>(k, v)))),
topBlockedDomains: List<Map<String, int>>.from(json["top_blocked_domains"].map((x) => Map.from(x).map((k, v) => MapEntry<String, int>(k, v)))), topBlockedDomains: List<Map<String, int>>.from(json["top_blocked_domains"].map((x) => Map.from(x).map((k, v) => MapEntry<String, int>(k, v)))),
topUpstreamResponses: json["top_upstreams_responses"] != null ? List<Map<String, int>>.from(json["top_upstreams_responses"].map((x) => Map.from(x).map((k, v) => MapEntry<String, int>(k, v)))) : null,
topUpstreamsAvgTime: json["top_upstreams_avg_time"] != null ? List<Map<String, double>>.from(json["top_upstreams_avg_time"].map((x) => Map.from(x).map((k, v) => MapEntry<String, double>(k, v)))) : null,
dnsQueries: List<int>.from(json["dns_queries"].map((x) => x)), dnsQueries: List<int>.from(json["dns_queries"].map((x) => x)),
blockedFiltering: List<int>.from(json["blocked_filtering"].map((x) => x)), blockedFiltering: List<int>.from(json["blocked_filtering"].map((x) => x)),
replacedSafebrowsing: List<int>.from(json["replaced_safebrowsing"].map((x) => x)), replacedSafebrowsing: List<int>.from(json["replaced_safebrowsing"].map((x) => x)),
@ -59,6 +65,8 @@ class DnsStatistics {
"top_queried_domains": List<dynamic>.from(topQueriedDomains.map((x) => Map.from(x).map((k, v) => MapEntry<String, dynamic>(k, v)))), "top_queried_domains": List<dynamic>.from(topQueriedDomains.map((x) => Map.from(x).map((k, v) => MapEntry<String, dynamic>(k, v)))),
"top_clients": List<dynamic>.from(topClients.map((x) => Map.from(x).map((k, v) => MapEntry<String, dynamic>(k, v)))), "top_clients": List<dynamic>.from(topClients.map((x) => Map.from(x).map((k, v) => MapEntry<String, dynamic>(k, v)))),
"top_blocked_domains": List<dynamic>.from(topBlockedDomains.map((x) => Map.from(x).map((k, v) => MapEntry<String, dynamic>(k, v)))), "top_blocked_domains": List<dynamic>.from(topBlockedDomains.map((x) => Map.from(x).map((k, v) => MapEntry<String, dynamic>(k, v)))),
"top_upstreams_responses": topUpstreamResponses != null ? List<dynamic>.from(topUpstreamResponses!.map((x) => Map.from(x).map((k, v) => MapEntry<String, dynamic>(k, v)))) : null,
"top_upstreams_avg_time": topUpstreamsAvgTime != null ? List<dynamic>.from(topUpstreamsAvgTime!.map((x) => Map.from(x).map((k, v) => MapEntry<String, dynamic>(k, v)))) : null,
"dns_queries": List<dynamic>.from(dnsQueries.map((x) => x)), "dns_queries": List<dynamic>.from(dnsQueries.map((x) => x)),
"blocked_filtering": List<dynamic>.from(blockedFiltering.map((x) => x)), "blocked_filtering": List<dynamic>.from(blockedFiltering.map((x) => x)),
"replaced_safebrowsing": List<dynamic>.from(replacedSafebrowsing.map((x) => x)), "replaced_safebrowsing": List<dynamic>.from(replacedSafebrowsing.map((x) => x)),

View file

@ -122,3 +122,128 @@ class EncryptionData {
"private_key_saved": privateKeySaved, "private_key_saved": privateKeySaved,
}; };
} }
class EncryptionValidationResult {
final bool isObject;
final EncryptionValidation? encryptionValidation;
final String? message;
const EncryptionValidationResult({
required this.isObject,
this.encryptionValidation,
this.message
});
}
class EncryptionValidation {
final String? subject;
final String? issuer;
final String? keyType;
final DateTime? notBefore;
final DateTime? notAfter;
final String? warningValidation;
final List<String>? dnsNames;
final bool? validCert;
final bool? validChain;
final bool? validKey;
final bool? validPair;
final bool? enabled;
final String? serverName;
final bool? forceHttps;
final int? portHttps;
final int? portDnsOverTls;
final int? portDnsOverQuic;
final int? portDnscrypt;
final String? dnscryptConfigFile;
final bool? allowUnencryptedDoh;
final String? certificateChain;
final String? privateKey;
final String? certificatePath;
final String? privateKeyPath;
final bool? privateKeySaved;
EncryptionValidation({
this.subject,
this.issuer,
this.keyType,
this.notBefore,
this.notAfter,
this.warningValidation,
this.dnsNames,
this.validCert,
this.validChain,
this.validKey,
this.validPair,
this.enabled,
this.serverName,
this.forceHttps,
this.portHttps,
this.portDnsOverTls,
this.portDnsOverQuic,
this.portDnscrypt,
this.dnscryptConfigFile,
this.allowUnencryptedDoh,
this.certificateChain,
this.privateKey,
this.certificatePath,
this.privateKeyPath,
this.privateKeySaved,
});
factory EncryptionValidation.fromJson(Map<String, dynamic> json) => EncryptionValidation(
subject: json["subject"],
issuer: json["issuer"],
keyType: json["key_type"],
notBefore: json["not_before"] == null ? null : DateTime.parse(json["not_before"]),
notAfter: json["not_after"] == null ? null : DateTime.parse(json["not_after"]),
warningValidation: json["warning_validation"],
dnsNames: json["dns_names"] == null ? [] : List<String>.from(json["dns_names"]!.map((x) => x)),
validCert: json["valid_cert"],
validChain: json["valid_chain"],
validKey: json["valid_key"],
validPair: json["valid_pair"],
enabled: json["enabled"],
serverName: json["server_name"],
forceHttps: json["force_https"],
portHttps: json["port_https"],
portDnsOverTls: json["port_dns_over_tls"],
portDnsOverQuic: json["port_dns_over_quic"],
portDnscrypt: json["port_dnscrypt"],
dnscryptConfigFile: json["dnscrypt_config_file"],
allowUnencryptedDoh: json["allow_unencrypted_doh"],
certificateChain: json["certificate_chain"],
privateKey: json["private_key"],
certificatePath: json["certificate_path"],
privateKeyPath: json["private_key_path"],
privateKeySaved: json["private_key_saved"],
);
Map<String, dynamic> toJson() => {
"subject": subject,
"issuer": issuer,
"key_type": keyType,
"not_before": notBefore?.toIso8601String(),
"not_after": notAfter?.toIso8601String(),
"warning_validation": warningValidation,
"dns_names": dnsNames == null ? [] : List<dynamic>.from(dnsNames!.map((x) => x)),
"valid_cert": validCert,
"valid_chain": validChain,
"valid_key": validKey,
"valid_pair": validPair,
"enabled": enabled,
"server_name": serverName,
"force_https": forceHttps,
"port_https": portHttps,
"port_dns_over_tls": portDnsOverTls,
"port_dns_over_quic": portDnsOverQuic,
"port_dnscrypt": portDnscrypt,
"dnscrypt_config_file": dnscryptConfigFile,
"allow_unencrypted_doh": allowUnencryptedDoh,
"certificate_chain": certificateChain,
"private_key": privateKey,
"certificate_path": certificatePath,
"private_key_path": privateKeyPath,
"private_key_saved": privateKeySaved,
};
}

View file

@ -1,6 +1,6 @@
class Filtering { class Filtering {
final List<Filter> filters; List<Filter> filters;
final List<Filter> whitelistFilters; List<Filter> whitelistFilters;
List<String> userRules; List<String> userRules;
List<String> blockedServices; List<String> blockedServices;
int interval; int interval;
@ -40,7 +40,7 @@ class Filter {
final DateTime? lastUpdated; final DateTime? lastUpdated;
final int id; final int id;
final int rulesCount; final int rulesCount;
final bool enabled; bool enabled;
Filter({ Filter({
required this.url, required this.url,
@ -69,3 +69,13 @@ class Filter {
"enabled": enabled, "enabled": enabled,
}; };
} }
class ProcessedList {
final Filter list;
final bool successful;
const ProcessedList({
required this.list,
required this.successful
});
}

View file

@ -52,6 +52,8 @@ class AppConfigProvider with ChangeNotifier {
int _combinedChartHome = 0; int _combinedChartHome = 0;
int _showTopItemsChart = 0;
String? _doNotRememberVersion; String? _doNotRememberVersion;
GitHubRelease? _appUpdatesAvailable; GitHubRelease? _appUpdatesAvailable;
@ -168,6 +170,10 @@ class AppConfigProvider with ChangeNotifier {
return _hideServerAddress == 1 ? true : false; return _hideServerAddress == 1 ? true : false;
} }
bool get showTopItemsChart {
return _showTopItemsChart == 1 ? true : false;
}
void setDbInstance(Database db) { void setDbInstance(Database db) {
_dbInstance = db; _dbInstance = db;
} }
@ -402,6 +408,22 @@ class AppConfigProvider with ChangeNotifier {
} }
} }
Future<bool> setShowTopItemsChart(bool value) async {
final updated = await updateConfigQuery(
db: _dbInstance!,
column: 'showTopItemsChart',
value: value == true ? 1 : 0
);
if (updated == true) {
_showTopItemsChart = value == true ? 1 : 0;
notifyListeners();
return true;
}
else {
return false;
}
}
Future<bool> setDoNotRememberVersion(String value) async { Future<bool> setDoNotRememberVersion(String value) async {
final updated = await updateConfigQuery( final updated = await updateConfigQuery(
@ -424,9 +446,10 @@ class AppConfigProvider with ChangeNotifier {
_showIpLogs = dbData['showIpLogs'] ?? 0; _showIpLogs = dbData['showIpLogs'] ?? 0;
_combinedChartHome = dbData['combinedChart'] ?? 0; _combinedChartHome = dbData['combinedChart'] ?? 0;
_hideServerAddress = dbData['hideServerAddress']; _hideServerAddress = dbData['hideServerAddress'];
_showTopItemsChart = dbData['showTopItemsChart'];
if (dbData['homeTopItemsOrder'] != null) { if (dbData['homeTopItemsOrder'] != null) {
try { try {
_homeTopItemsOrder = List<HomeTopItems>.from( final itemsOrder = List<HomeTopItems>.from(
List<String>.from(jsonDecode(dbData['homeTopItemsOrder'])).map((e) { List<String>.from(jsonDecode(dbData['homeTopItemsOrder'])).map((e) {
switch (e) { switch (e) {
case 'queriedDomains': case 'queriedDomains':
@ -438,11 +461,22 @@ class AppConfigProvider with ChangeNotifier {
case 'recurrentClients': case 'recurrentClients':
return HomeTopItems.recurrentClients; return HomeTopItems.recurrentClients;
case 'topUpstreams':
return HomeTopItems.topUpstreams;
case 'avgUpstreamResponseTime':
return HomeTopItems.avgUpstreamResponseTime;
default: default:
return null; return null;
} }
}).where((e) => e != null).toList() }).where((e) => e != null).toList()
); );
final missingItems = homeTopItemsDefaultOrder.where((e) => !itemsOrder.contains(e));
_homeTopItemsOrder = [
...itemsOrder,
...missingItems
];
} catch (e) { } catch (e) {
Sentry.captureException(e); Sentry.captureException(e);
_homeTopItemsOrder = homeTopItemsDefaultOrder; _homeTopItemsOrder = homeTopItemsDefaultOrder;

View file

@ -1,20 +1,20 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:adguard_home_manager/services/api_client.dart';
import 'package:adguard_home_manager/models/clients.dart'; import 'package:adguard_home_manager/models/clients.dart';
import 'package:adguard_home_manager/functions/compare_versions.dart';
import 'package:adguard_home_manager/functions/maps_fns.dart'; import 'package:adguard_home_manager/functions/maps_fns.dart';
import 'package:adguard_home_manager/providers/status_provider.dart'; import 'package:adguard_home_manager/providers/status_provider.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart'; import 'package:adguard_home_manager/providers/servers_provider.dart';
import 'package:adguard_home_manager/models/clients_allowed_blocked.dart'; import 'package:adguard_home_manager/models/clients_allowed_blocked.dart';
import 'package:adguard_home_manager/constants/enums.dart'; import 'package:adguard_home_manager/constants/enums.dart';
enum AccessSettingsList { allowed, disallowed, domains }
class ClientsProvider with ChangeNotifier { class ClientsProvider with ChangeNotifier {
ServersProvider? _serversProvider; ServersProvider? _serversProvider;
StatusProvider? _statusProvider;
update(ServersProvider? servers, StatusProvider? status) { update(ServersProvider? servers, StatusProvider? status) {
_serversProvider = servers; _serversProvider = servers;
_statusProvider = status;
} }
LoadStatus _loadStatus = LoadStatus.loading; LoadStatus _loadStatus = LoadStatus.loading;
@ -103,9 +103,9 @@ class ClientsProvider with ChangeNotifier {
if (updateLoading == true) { if (updateLoading == true) {
_loadStatus = LoadStatus.loading; _loadStatus = LoadStatus.loading;
} }
final result = await _serversProvider!.apiClient!.getClients(); final result = await _serversProvider!.apiClient2!.getClients();
if (result['result'] == 'success') { if (result.successful == true) {
setClientsData(result['data'], false); setClientsData(result.content as Clients, false);
_loadStatus = LoadStatus.loaded; _loadStatus = LoadStatus.loaded;
notifyListeners(); notifyListeners();
return true; return true;
@ -120,9 +120,9 @@ class ClientsProvider with ChangeNotifier {
} }
Future<bool> deleteClient(Client client) async { Future<bool> deleteClient(Client client) async {
final result = await _serversProvider!.apiClient!.postDeleteClient(name: client.name); final result = await _serversProvider!.apiClient2!.postDeleteClient(name: client.name);
if (result['result'] == 'success') { if (result.successful == true) {
Clients clientsData = clients!; Clients clientsData = clients!;
clientsData.clients = clientsData.clients.where((c) => c.name != client.name).toList(); clientsData.clients = clientsData.clients.where((c) => c.name != client.name).toList();
setClientsData(clientsData, false); setClientsData(clientsData, false);
@ -136,20 +136,14 @@ class ClientsProvider with ChangeNotifier {
} }
Future<bool> editClient(Client client) async { Future<bool> editClient(Client client) async {
final result = await _serversProvider!.apiClient!.postUpdateClient( final result = await _serversProvider!.apiClient2!.postUpdateClient(
data: { data: {
'name': client.name, 'name': client.name,
'data': serverVersionIsAhead( 'data': removePropFromMap(client.toJson(), 'safe_search')
currentVersion: _statusProvider!.serverStatus!.serverVersion,
referenceVersion: 'v0.107.28',
referenceVersionBeta: 'v0.108.0-b.33'
) == false
? removePropFromMap(client.toJson(), 'safesearch_enabled')
: removePropFromMap(client.toJson(), 'safe_search')
} }
); );
if (result['result'] == 'success') { if (result.successful == true) {
Clients clientsData = clients!; Clients clientsData = clients!;
clientsData.clients = clientsData.clients.map((e) { clientsData.clients = clientsData.clients.map((e) {
if (e.name == client.name) { if (e.name == client.name) {
@ -171,17 +165,11 @@ class ClientsProvider with ChangeNotifier {
} }
Future<bool> addClient(Client client) async { Future<bool> addClient(Client client) async {
final result = await _serversProvider!.apiClient!.postAddClient( final result = await _serversProvider!.apiClient2!.postAddClient(
data: serverVersionIsAhead( data: removePropFromMap(client.toJson(), 'safe_search')
currentVersion: _statusProvider!.serverStatus!.serverVersion,
referenceVersion: 'v0.107.28',
referenceVersionBeta: 'v0.108.0-b.33'
) == false
? removePropFromMap(client.toJson(), 'safesearch_enabled')
: removePropFromMap(client.toJson(), 'safe_search')
); );
if (result['result'] == 'success') { if (result.successful == true) {
Clients clientsData = clients!; Clients clientsData = clients!;
clientsData.clients.add(client); clientsData.clients.add(client);
setClientsData(clientsData, false); setClientsData(clientsData, false);
@ -195,91 +183,105 @@ class ClientsProvider with ChangeNotifier {
} }
} }
Future<Map<String, dynamic>> addClientList(String item, String type) async { Future<ApiResponse> addClientList(String item, AccessSettingsList type) async {
Map<String, List<String>> body = { Map<String, List<String>> body = {
"allowed_clients": clients!.clientsAllowedBlocked?.allowedClients ?? [], "allowed_clients": clients!.clientsAllowedBlocked?.allowedClients ?? [],
"disallowed_clients": clients!.clientsAllowedBlocked?.disallowedClients ?? [], "disallowed_clients": clients!.clientsAllowedBlocked?.disallowedClients ?? [],
"blocked_hosts": clients!.clientsAllowedBlocked?.blockedHosts ?? [], "blocked_hosts": clients!.clientsAllowedBlocked?.blockedHosts ?? [],
}; };
if (type == 'allowed') { if (body['allowed_clients']!.contains(item)) {
body['allowed_clients'] = body['allowed_clients']!.where((e) => e != item).toList();
}
else if (body['disallowed_clients']!.contains(item)) {
body['disallowed_clients'] = body['disallowed_clients']!.where((e) => e != item).toList();
}
else if (body['blocked_hosts']!.contains(item)) {
body['blocked_hosts'] = body['blocked_hosts']!.where((e) => e != item).toList();
}
if (type == AccessSettingsList.allowed) {
body['allowed_clients']!.add(item); body['allowed_clients']!.add(item);
} }
else if (type == 'disallowed') { else if (type == AccessSettingsList.disallowed) {
body['disallowed_clients']!.add(item); body['disallowed_clients']!.add(item);
} }
else if (type == 'domains') { else if (type == AccessSettingsList.domains) {
body['blocked_hosts']!.add(item); body['blocked_hosts']!.add(item);
} }
final result = await _serversProvider!.apiClient!.requestAllowedBlockedClientsHosts(body); final result = await _serversProvider!.apiClient2!.requestAllowedBlockedClientsHosts(
body: body
);
if (result['result'] == 'success') { if (result.successful == true) {
_clients?.clientsAllowedBlocked = ClientsAllowedBlocked( _clients?.clientsAllowedBlocked = ClientsAllowedBlocked(
allowedClients: body['allowed_clients'] ?? [], allowedClients: body['allowed_clients'] ?? [],
disallowedClients: body['disallowed_clients'] ?? [], disallowedClients: body['disallowed_clients'] ?? [],
blockedHosts: body['blocked_hosts'] ?? [], blockedHosts: body['blocked_hosts'] ?? [],
); );
notifyListeners(); notifyListeners();
return { 'success': true }; return result;
} }
else if (result['result'] == 'error' && result['message'] == 'client_another_list') { else if (result.successful == false && result.content == 'client_another_list') {
notifyListeners(); notifyListeners();
return { return result;
'success': false,
'error': 'client_another_list'
};
} }
else { else {
notifyListeners(); notifyListeners();
return { return result;
'success': false,
'error': null
};
} }
} }
Future<Map<String, dynamic>> removeClientList(String client, String type) async { AccessSettingsList? checkClientList(String client) {
if (_clients!.clientsAllowedBlocked!.allowedClients.contains(client)) {
return AccessSettingsList.allowed;
}
else if (_clients!.clientsAllowedBlocked!.disallowedClients.contains(client)) {
return AccessSettingsList.disallowed;
}
else {
return null;
}
}
Future<ApiResponse> removeClientList(String client, AccessSettingsList type) async {
Map<String, List<String>> body = { Map<String, List<String>> body = {
"allowed_clients": clients!.clientsAllowedBlocked?.allowedClients ?? [], "allowed_clients": clients!.clientsAllowedBlocked?.allowedClients ?? [],
"disallowed_clients": clients!.clientsAllowedBlocked?.disallowedClients ?? [], "disallowed_clients": clients!.clientsAllowedBlocked?.disallowedClients ?? [],
"blocked_hosts": clients!.clientsAllowedBlocked?.blockedHosts ?? [], "blocked_hosts": clients!.clientsAllowedBlocked?.blockedHosts ?? [],
}; };
if (type == 'allowed') { if (type == AccessSettingsList.allowed) {
body['allowed_clients'] = body['allowed_clients']!.where((c) => c != client).toList(); body['allowed_clients'] = body['allowed_clients']!.where((c) => c != client).toList();
} }
else if (type == 'disallowed') { else if (type == AccessSettingsList.disallowed) {
body['disallowed_clients'] = body['disallowed_clients']!.where((c) => c != client).toList(); body['disallowed_clients'] = body['disallowed_clients']!.where((c) => c != client).toList();
} }
else if (type == 'domains') { else if (type == AccessSettingsList.domains) {
body['blocked_hosts'] = body['blocked_hosts']!.where((c) => c != client).toList(); body['blocked_hosts'] = body['blocked_hosts']!.where((c) => c != client).toList();
} }
final result = await _serversProvider!.apiClient!.requestAllowedBlockedClientsHosts(body); final result = await _serversProvider!.apiClient2!.requestAllowedBlockedClientsHosts(
body: body
);
if (result['result'] == 'success') { if (result.successful == true) {
_clients?.clientsAllowedBlocked = ClientsAllowedBlocked( _clients?.clientsAllowedBlocked = ClientsAllowedBlocked(
allowedClients: body['allowed_clients'] ?? [], allowedClients: body['allowed_clients'] ?? [],
disallowedClients: body['disallowed_clients'] ?? [], disallowedClients: body['disallowed_clients'] ?? [],
blockedHosts: body['blocked_hosts'] ?? [], blockedHosts: body['blocked_hosts'] ?? [],
); );
notifyListeners(); notifyListeners();
return { 'success': true }; return result;
} }
else if (result['result'] == 'error' && result['message'] == 'client_another_list') { else if (result.successful == false && result.content == 'client_another_list') {
notifyListeners(); notifyListeners();
return { return result;
'success': false,
'error': 'client_another_list'
};
} }
else { else {
notifyListeners(); notifyListeners();
return { return result;
'success': false,
'error': null
};
} }
} }
} }

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:adguard_home_manager/services/api_client.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart'; import 'package:adguard_home_manager/providers/servers_provider.dart';
import 'package:adguard_home_manager/constants/enums.dart'; import 'package:adguard_home_manager/constants/enums.dart';
import 'package:adguard_home_manager/models/dhcp.dart'; import 'package:adguard_home_manager/models/dhcp.dart';
@ -41,9 +42,9 @@ class DhcpProvider with ChangeNotifier {
_loadStatus = LoadStatus.loading; _loadStatus = LoadStatus.loading;
notifyListeners(); notifyListeners();
} }
final result = await _serversProvider!.apiClient!.getDhcpData(); final result = await _serversProvider!.apiClient2!.getDhcpData();
if (result['result'] == 'success') { if (result.successful == true) {
_dhcp = result['data']; _dhcp = result.content as DhcpModel;
_loadStatus = LoadStatus.loaded; _loadStatus = LoadStatus.loaded;
notifyListeners(); notifyListeners();
return true; return true;
@ -58,7 +59,7 @@ class DhcpProvider with ChangeNotifier {
} }
Future<bool> deleteLease(Lease lease) async { Future<bool> deleteLease(Lease lease) async {
final result = await _serversProvider!.apiClient!.deleteStaticLease( final result = await _serversProvider!.apiClient2!.deleteStaticLease(
data: { data: {
"mac": lease.mac, "mac": lease.mac,
"ip": lease.ip, "ip": lease.ip,
@ -66,9 +67,9 @@ class DhcpProvider with ChangeNotifier {
} }
); );
if (result['result'] == 'success') { if (result.successful == true) {
DhcpModel data = dhcp!; DhcpModel data = dhcp!;
data.dhcpStatus.staticLeases = data.dhcpStatus.staticLeases.where((l) => l.mac != lease.mac).toList(); data.dhcpStatus!.staticLeases = data.dhcpStatus!.staticLeases.where((l) => l.mac != lease.mac).toList();
setDhcpData(data); setDhcpData(data);
return true; return true;
} }
@ -78,8 +79,8 @@ class DhcpProvider with ChangeNotifier {
} }
} }
Future<Map<String, dynamic>> createLease(Lease lease) async { Future<ApiResponse> createLease(Lease lease) async {
final result = await _serversProvider!.apiClient!.createStaticLease( final result = await _serversProvider!.apiClient2!.createStaticLease(
data: { data: {
"mac": lease.mac, "mac": lease.mac,
"ip": lease.ip, "ip": lease.ip,
@ -87,29 +88,14 @@ class DhcpProvider with ChangeNotifier {
} }
); );
if (result['result'] == 'success') { if (result.successful == true) {
DhcpModel data = dhcp!; DhcpModel data = dhcp!;
data.dhcpStatus.staticLeases.add(lease); data.dhcpStatus!.staticLeases.add(lease);
setDhcpData(data); setDhcpData(data);
return { 'success': true }; return result;
}
else if (result['result'] == 'error' && result['message'] == 'already_exists' ) {
return {
'success': false,
'error': 'already_exists'
};
}
else if (result['result'] == 'error' && result['message'] == 'server_not_configured' ) {
return {
'success': false,
'error': 'server_not_configured'
};
} }
else { else {
return { return result;
'success': false,
'error': null
};
} }
} }
} }

View file

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:adguard_home_manager/services/api_client.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart'; import 'package:adguard_home_manager/providers/servers_provider.dart';
import 'package:adguard_home_manager/constants/enums.dart'; import 'package:adguard_home_manager/constants/enums.dart';
import 'package:adguard_home_manager/models/dns_info.dart'; import 'package:adguard_home_manager/models/dns_info.dart';
@ -41,10 +42,10 @@ class DnsProvider with ChangeNotifier {
_loadStatus = LoadStatus.loading; _loadStatus = LoadStatus.loading;
} }
final result = await _serversProvider!.apiClient!.getDnsInfo(); final result = await _serversProvider!.apiClient2!.getDnsInfo();
if (result['result'] == 'success') { if (result.successful == true) {
_dnsInfo = result['data']; _dnsInfo = result.content as DnsInfo;
_loadStatus = LoadStatus.loaded; _loadStatus = LoadStatus.loaded;
notifyListeners(); notifyListeners();
return true; return true;
@ -58,12 +59,12 @@ class DnsProvider with ChangeNotifier {
} }
} }
Future<Map<String, dynamic>> savePrivateReverseServersConfig(Map<String, dynamic> value) async { Future<ApiResponse> savePrivateReverseServersConfig(Map<String, dynamic> value) async {
final result = await _serversProvider!.apiClient!.setDnsConfig( final result = await _serversProvider!.apiClient2!.setDnsConfig(
data: value data: value
); );
if (result['result'] == 'success') { if (result.successful == true) {
DnsInfo data = dnsInfo!; DnsInfo data = dnsInfo!;
if (value['local_ptr_upstreams'] != null) { if (value['local_ptr_upstreams'] != null) {
data.localPtrUpstreams = value['local_ptr_upstreams']; data.localPtrUpstreams = value['local_ptr_upstreams'];
@ -71,107 +72,87 @@ class DnsProvider with ChangeNotifier {
data.usePrivatePtrResolvers = value['use_private_ptr_resolvers']; data.usePrivatePtrResolvers = value['use_private_ptr_resolvers'];
data.resolveClients = value['resolve_clients']; data.resolveClients = value['resolve_clients'];
setDnsInfoData(data); setDnsInfoData(data);
return { 'success': true }; return result;
}
else if (result['log'] != null && result['log'].statusCode == '400') {
return {
'success': false,
'error': 400
};
} }
else { else {
return { return result;
'success': false, }
'error': null
};
}
} }
Future<Map<String, dynamic>> saveUpstreamDnsConfig(Map<String, dynamic> value) async { Future<ApiResponse> saveUpstreamDnsConfig(Map<String, dynamic> value) async {
final result = await _serversProvider!.apiClient!.setDnsConfig( final result = await _serversProvider!.apiClient2!.setDnsConfig(
data: value data: value
); );
if (result['result'] == 'success') { if (result.successful == true) {
DnsInfo data = dnsInfo!; DnsInfo data = dnsInfo!;
data.upstreamDns = List<String>.from(value['upstream_dns']); data.upstreamDns = List<String>.from(value['upstream_dns']);
data.upstreamMode = value['upstream_mode']; data.upstreamMode = value['upstream_mode'];
setDnsInfoData(data); setDnsInfoData(data);
return { 'success': true }; return result;
}
else if (result['log'] != null && result['log'].statusCode == '400') {
return {
'success': false,
'error': 400
};
} }
else { else {
return { return result;
'success': false, }
'error': null
};
}
} }
Future<Map<String, dynamic>> saveBootstrapDnsConfig(Map<String, dynamic> value) async { Future<ApiResponse> saveBootstrapDnsConfig(Map<String, dynamic> value) async {
final result = await _serversProvider!.apiClient!.setDnsConfig( final result = await _serversProvider!.apiClient2!.setDnsConfig(
data: value data: value
); );
if (result['result'] == 'success') { if (result.successful == true) {
DnsInfo data = dnsInfo!; DnsInfo data = dnsInfo!;
data.bootstrapDns = List<String>.from(value['bootstrap_dns']); data.bootstrapDns = List<String>.from(value['bootstrap_dns']);
setDnsInfoData(data); setDnsInfoData(data);
return { 'success': true }; return result;
}
else if (result['log'] != null && result['log'].statusCode == '400') {
return {
'success': false,
'error': 400
};
} }
else { else {
return { return result;
'success': false, }
'error': null
};
}
} }
Future<Map<String, dynamic>> saveCacheCacheConfig(Map<String, dynamic> value) async { Future<ApiResponse> saveFallbackDnsConfig(Map<String, dynamic> value) async {
final result = await _serversProvider!.apiClient!.setDnsConfig( final result = await _serversProvider!.apiClient2!.setDnsConfig(
data: value data: value
); );
if (result['result'] == 'success') { if (result.successful == true) {
DnsInfo data = dnsInfo!;
data.fallbackDns = List<String>.from(value['fallback_dns']);
setDnsInfoData(data);
return result;
}
else {
return result;
}
}
Future<ApiResponse> saveCacheCacheConfig(Map<String, dynamic> value) async {
final result = await _serversProvider!.apiClient2!.setDnsConfig(
data: value
);
if (result.successful == true) {
DnsInfo data = dnsInfo!; DnsInfo data = dnsInfo!;
data.cacheSize = value['cache_size']; data.cacheSize = value['cache_size'];
data.cacheTtlMin = value['cache_ttl_min']; data.cacheTtlMin = value['cache_ttl_min'];
data.cacheTtlMax = value['cache_ttl_max']; data.cacheTtlMax = value['cache_ttl_max'];
data.cacheOptimistic = value['cache_optimistic']; data.cacheOptimistic = value['cache_optimistic'];
setDnsInfoData(data); setDnsInfoData(data);
return { 'success': true }; return result;
}
else if (result['log'] != null && result['log'].statusCode == '400') {
return {
'success': false,
'error': 400
};
} }
else { else {
return { return result;
'success': false, }
'error': null
};
}
} }
Future<Map<String, dynamic>> saveDnsServerConfig(Map<String, dynamic> value) async { Future<ApiResponse> saveDnsServerConfig(Map<String, dynamic> value) async {
final result = await _serversProvider!.apiClient!.setDnsConfig( final result = await _serversProvider!.apiClient2!.setDnsConfig(
data: value data: value
); );
if (result['result'] == 'success') { if (result.successful == true) {
DnsInfo data = dnsInfo!; DnsInfo data = dnsInfo!;
data.ratelimit = value['ratelimit']; data.ratelimit = value['ratelimit'];
data.ednsCsEnabled = value['edns_cs_enabled']; data.ednsCsEnabled = value['edns_cs_enabled'];
@ -180,20 +161,12 @@ class DnsProvider with ChangeNotifier {
data.blockingMode = value['blocking_mode']; data.blockingMode = value['blocking_mode'];
data.blockingIpv4 = value['blocking_ipv4']; data.blockingIpv4 = value['blocking_ipv4'];
data.blockingIpv6 = value['blocking_ipv6']; data.blockingIpv6 = value['blocking_ipv6'];
data.blockedResponseTtl = value['blocked_response_ttl'];
setDnsInfoData(data); setDnsInfoData(data);
return { 'success': true }; return result;
}
else if (result['log'] != null && result['log'].statusCode == '400') {
return {
'success': false,
'error': 400
};
} }
else { else {
return { return result;
'success': false, }
'error': null
};
}
} }
} }

View file

@ -88,10 +88,10 @@ class FilteringProvider with ChangeNotifier {
_blockedServicesLoadStatus = LoadStatus.loading; _blockedServicesLoadStatus = LoadStatus.loading;
if (showLoader == true) notifyListeners(); if (showLoader == true) notifyListeners();
final result = await _serversProvider!.apiClient!.getBlockedServices(); final result = await _serversProvider!.apiClient2!.getBlockedServices();
if (result['result'] == 'success') { if (result.successful == true) {
_blockedServicesLoadStatus = LoadStatus.loaded; _blockedServicesLoadStatus = LoadStatus.loaded;
_blockedServicesList = BlockedServices(services: result['data']); _blockedServicesList = BlockedServices(services: result.content as List<BlockedService>);
notifyListeners(); notifyListeners();
return true; return true;
@ -112,9 +112,9 @@ class FilteringProvider with ChangeNotifier {
_loadStatus = LoadStatus.loading; _loadStatus = LoadStatus.loading;
} }
final result = await _serversProvider!.apiClient!.getFiltering(); final result = await _serversProvider!.apiClient2!.getFiltering();
if (result['result'] == 'success') { if (result.successful == true) {
_filtering = result['data']; _filtering = result.content as Filtering;
_loadStatus = LoadStatus.loaded; _loadStatus = LoadStatus.loaded;
notifyListeners(); notifyListeners();
return true; return true;
@ -127,15 +127,16 @@ class FilteringProvider with ChangeNotifier {
} }
Future<Map<String, dynamic>> updateLists() async { Future<Map<String, dynamic>> updateLists() async {
final result = await _serversProvider!.apiClient!.updateLists(); final result = await _serversProvider!.apiClient2!.updateLists();
if (result['result'] == 'success') { if (result.successful == true) {
final result2 = await _serversProvider!.apiClient!.getFiltering(); final result2 = await _serversProvider!.apiClient2!.getFiltering();
if (result2['result'] == 'success') { if (result2.successful == true) {
_filtering = result2['data']; _filtering = result2.content as Filtering;
notifyListeners(); notifyListeners();
print(result.content);
return { return {
"success": true, "success": true,
"data": result['data'] "data": result.content
}; };
} }
else { else {
@ -151,10 +152,10 @@ class FilteringProvider with ChangeNotifier {
Future<bool> enableDisableFiltering() async { Future<bool> enableDisableFiltering() async {
final newValue = !_statusProvider!.serverStatus!.filteringEnabled; final newValue = !_statusProvider!.serverStatus!.filteringEnabled;
final result = await _serversProvider!.apiClient!.updateFiltering( final result = await _serversProvider!.apiClient2!.updateFiltering(
enable: newValue enable: newValue
); );
if (result['result'] == 'success') { if (result.successful == true) {
setFilteringProtectionStatus(newValue, false); setFilteringProtectionStatus(newValue, false);
notifyListeners(); notifyListeners();
return true; return true;
@ -166,13 +167,13 @@ class FilteringProvider with ChangeNotifier {
} }
Future<bool> changeUpdateFrequency(int value) async { Future<bool> changeUpdateFrequency(int value) async {
final result = await _serversProvider!.apiClient!.requestChangeUpdateFrequency( final result = await _serversProvider!.apiClient2!.requestChangeUpdateFrequency(
data: { data: {
"enabled": filtering!.enabled, "enabled": filtering!.enabled,
"interval": value "interval": value
} }
); );
if (result['result'] == 'success') { if (result.successful == true) {
setFiltersUpdateFrequency(value); setFiltersUpdateFrequency(value);
return true; return true;
} }
@ -185,9 +186,9 @@ class FilteringProvider with ChangeNotifier {
Future<bool> removeCustomRule(String rule) async { Future<bool> removeCustomRule(String rule) async {
final List<String> newRules = filtering!.userRules.where((r) => r != rule).toList(); final List<String> newRules = filtering!.userRules.where((r) => r != rule).toList();
final result = await _serversProvider!.apiClient!.setCustomRules(rules: newRules); final result = await _serversProvider!.apiClient2!.setCustomRules(rules: newRules);
if (result['result'] == 'success') { if (result.successful == true) {
Filtering filteringData = filtering!; Filtering filteringData = filtering!;
filteringData.userRules = newRules; filteringData.userRules = newRules;
_filtering = filteringData; _filtering = filteringData;
@ -205,18 +206,18 @@ class FilteringProvider with ChangeNotifier {
required String listUrl, required String listUrl,
required String type required String type
}) async { }) async {
final result1 = await _serversProvider!.apiClient!.deleteFilterList( final result1 = await _serversProvider!.apiClient2!.deleteFilterList(
data: { data: {
"url": listUrl, "url": listUrl,
"whitelist": type == 'whitelist' ? true : false "whitelist": type == 'whitelist' ? true : false
} }
); );
if (result1['result'] == 'success') { if (result1.successful == true) {
final result2 = await _serversProvider!.apiClient!.getFiltering(); final result2 = await _serversProvider!.apiClient2!.getFiltering();
if (result2['result'] == 'success') { if (result2.successful == true) {
_filtering = result2['data']; _filtering = result2.content as Filtering;
notifyListeners(); notifyListeners();
return true; return true;
} }
@ -236,7 +237,7 @@ class FilteringProvider with ChangeNotifier {
required String type, required String type,
required FilteringListActions action required FilteringListActions action
}) async { }) async {
final result1 = await _serversProvider!.apiClient!.updateFilterList( final result1 = await _serversProvider!.apiClient2!.updateFilterList(
data: { data: {
"data": { "data": {
"enabled": action == FilteringListActions.disable || action == FilteringListActions.enable "enabled": action == FilteringListActions.disable || action == FilteringListActions.enable
@ -250,11 +251,11 @@ class FilteringProvider with ChangeNotifier {
} }
); );
if (result1['result'] == 'success') { if (result1.successful == true) {
final result2 = await _serversProvider!.apiClient!.getFiltering(); final result2 = await _serversProvider!.apiClient2!.getFiltering();
if (result2['result'] == 'success') { if (result2.successful == true) {
_filtering = result2['data']; _filtering = result2.content as Filtering;
notifyListeners(); notifyListeners();
return true; return true;
} }
@ -273,9 +274,9 @@ class FilteringProvider with ChangeNotifier {
final List<String> newRules = filtering!.userRules; final List<String> newRules = filtering!.userRules;
newRules.add(rule); newRules.add(rule);
final result = await _serversProvider!.apiClient!.setCustomRules(rules: newRules); final result = await _serversProvider!.apiClient2!.setCustomRules(rules: newRules);
if (result['result'] == 'success') { if (result.successful == true) {
Filtering filteringData = filtering!; Filtering filteringData = filtering!;
filteringData.userRules = newRules; filteringData.userRules = newRules;
_filtering = filteringData; _filtering = filteringData;
@ -287,8 +288,9 @@ class FilteringProvider with ChangeNotifier {
return false; return false;
} }
} }
Future<Map<String, dynamic>> addList({required String name, required String url, required String type}) async { Future<Map<String, dynamic>> addList({required String name, required String url, required String type}) async {
final result1 = await _serversProvider!.apiClient!.addFilteringList( final result1 = await _serversProvider!.apiClient2!.addFilteringList(
data: { data: {
'name': name, 'name': name,
'url': url, 'url': url,
@ -296,13 +298,13 @@ class FilteringProvider with ChangeNotifier {
} }
); );
if (result1['result'] == 'success') { if (result1.successful == true) {
if (result1['data'].toString().contains("OK")) { if (result1.content.toString().contains("OK")) {
final result2 = await _serversProvider!.apiClient!.getFiltering(); final result2 = await _serversProvider!.apiClient2!.getFiltering();
final items = result1['data'].toString().split(' ')[1]; final items = result1.content.toString().split(' ')[1];
if (result2['result'] == 'success') { if (result2.successful == true) {
_filtering = result2['data']; _filtering = result2.content as Filtering;
notifyListeners(); notifyListeners();
return { return {
'success': true, 'success': true,
@ -325,14 +327,14 @@ class FilteringProvider with ChangeNotifier {
}; };
} }
} }
else if (result1['result'] == 'error' && result1['log'].statusCode == '400' && result1['log'].resBody.toString().contains("Couldn't fetch filter from url")) { else if (result1.successful == false && result1.statusCode == 400 && result1.content.toString().contains("data is HTML, not plain text")) {
notifyListeners(); notifyListeners();
return { return {
'success': false, 'success': false,
'error': 'invalid_url' 'error': 'invalid_url'
}; };
} }
else if (result1['result'] == 'error' && result1['log'].statusCode == '400' && result1['log'].resBody.toString().contains('Filter URL already added')) { else if (result1.successful == false && result1.statusCode == 400 && result1.content.toString().contains('url already exists')) {
notifyListeners(); notifyListeners();
return { return {
'success': false, 'success': false,
@ -355,9 +357,9 @@ class FilteringProvider with ChangeNotifier {
_blockedServicesLoadStatus = LoadStatus.loading; _blockedServicesLoadStatus = LoadStatus.loading;
} }
final result = await _serversProvider!.apiClient!.getBlockedServices(); final result = await _serversProvider!.apiClient2!.getBlockedServices();
if (result['result'] == 'success') { if (result.successful == true) {
_blockedServicesList = BlockedServices(services: result['data']); _blockedServicesList = BlockedServices(services: result.content as List<BlockedService>);
_blockedServicesLoadStatus = LoadStatus.loaded; _blockedServicesLoadStatus = LoadStatus.loaded;
notifyListeners(); notifyListeners();
@ -371,11 +373,11 @@ class FilteringProvider with ChangeNotifier {
} }
Future<bool> updateBlockedServices(List<String> values) async { Future<bool> updateBlockedServices(List<String> values) async {
final result = await _serversProvider!.apiClient!.setBlockedServices( final result = await _serversProvider!.apiClient2!.setBlockedServices(
data: values data: values
); );
if (result['result'] == 'success') { if (result.successful == true) {
setBlockedServices(values); setBlockedServices(values);
return true; return true;
} }
@ -384,4 +386,75 @@ class FilteringProvider with ChangeNotifier {
return false; return false;
} }
} }
Future<List<ProcessedList>> deleteMultipleLists({
required List<Filter> blacklists,
required List<Filter> whitelists
}) async {
Future<ProcessedList> deleteList({
required Filter list,
required bool isWhitelist,
}) async {
final result = await _serversProvider!.apiClient2!.deleteFilterList(
data: {
"url": list.url,
"whitelist": isWhitelist
}
);
if (result.successful == true) {
return ProcessedList(list: list, successful: true);
}
else {
return ProcessedList(list: list, successful: false);
}
}
final resultWhitelists = await Future.wait(whitelists.map((e) => deleteList(list: e, isWhitelist: true)));
final resultBlacklists = await Future.wait(blacklists.map((e) => deleteList(list: e, isWhitelist: false)));
await fetchFilters();
return [
...resultWhitelists,
...resultBlacklists,
];
}
Future<List<ProcessedList>> enableDisableMultipleLists({
required List<Filter> blacklists,
required List<Filter> whitelists
}) async {
Future<ProcessedList> enableDisableList({
required Filter list,
required bool isWhitelist,
}) async {
final result = await _serversProvider!.apiClient2!.updateFilterList(
data: {
"data": {
"enabled": !list.enabled,
"name": list.name,
"url": list.url
},
"url": list.url,
"whitelist": isWhitelist
}
);
if (result.successful == true) {
return ProcessedList(list: list, successful: true);
}
else {
return ProcessedList(list: list, successful: false);
}
}
final resultWhitelists = await Future.wait(whitelists.map((e) => enableDisableList(list: e, isWhitelist: true)));
final resultBlacklists = await Future.wait(blacklists.map((e) => enableDisableList(list: e, isWhitelist: false)));
await fetchFilters();
return [
...resultWhitelists,
...resultBlacklists,
];
}
} }

View file

@ -114,9 +114,15 @@ class LogsProvider with ChangeNotifier {
_offset = value; _offset = value;
} }
void setSelectedResultStatus(String value) { void setSelectedResultStatus({
required String value,
bool? refetch
}) {
_selectedResultStatus = value; _selectedResultStatus = value;
notifyListeners(); notifyListeners();
if (refetch = true) {
filterLogs();
}
} }
void setSearchText(String? value) { void setSearchText(String? value) {
@ -153,7 +159,7 @@ class LogsProvider with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
final result = await _serversProvider!.apiClient!.getLogs( final result = await _serversProvider!.apiClient2!.getLogs(
count: logsQuantity, count: logsQuantity,
offset: offst, offset: offst,
olderThan: logsOlderThan, olderThan: logsOlderThan,
@ -166,11 +172,11 @@ class LogsProvider with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
if (result['result'] == 'success') { if (result.successful == true) {
_offset = inOffset != null ? inOffset+logsQuantity : offset+logsQuantity; _offset = inOffset != null ? inOffset+logsQuantity : offset+logsQuantity;
if (loadingMore != null && loadingMore == true && logsData != null) { if (loadingMore != null && loadingMore == true && logsData != null) {
LogsData newLogsData = result['data']; LogsData newLogsData = result.content;
newLogsData.data = [...logsData!.data, ...result['data'].data]; newLogsData.data = [...logsData!.data, ...(result.content as LogsData).data];
if (appliedFilters.clients != null) { if (appliedFilters.clients != null) {
newLogsData.data = newLogsData.data.where( newLogsData.data = newLogsData.data.where(
(item) => appliedFilters.clients!.contains(item.client) (item) => appliedFilters.clients!.contains(item.client)
@ -179,7 +185,7 @@ class LogsProvider with ChangeNotifier {
_logsData = newLogsData; _logsData = newLogsData;
} }
else { else {
LogsData newLogsData = result['data']; LogsData newLogsData = result.content;
if (appliedFilters.clients != null) { if (appliedFilters.clients != null) {
newLogsData.data = newLogsData.data.where( newLogsData.data = newLogsData.data.where(
(item) => appliedFilters.clients!.contains(item.client) (item) => appliedFilters.clients!.contains(item.client)
@ -204,7 +210,7 @@ class LogsProvider with ChangeNotifier {
resetFilters(); resetFilters();
final result = await _serversProvider!.apiClient!.getLogs( final result = await _serversProvider!.apiClient2!.getLogs(
count: logsQuantity count: logsQuantity
); );
@ -214,8 +220,8 @@ class LogsProvider with ChangeNotifier {
clients: null clients: null
); );
if (result['result'] == 'success') { if (result.successful == true) {
_logsData = result['data']; _logsData = result.content as LogsData;
_loadStatus = LoadStatus.loaded; _loadStatus = LoadStatus.loaded;
notifyListeners(); notifyListeners();
return true; return true;
@ -233,7 +239,7 @@ class LogsProvider with ChangeNotifier {
setOffset(0); setOffset(0);
final result = await _serversProvider!.apiClient!.getLogs( final result = await _serversProvider!.apiClient2!.getLogs(
count: logsQuantity, count: logsQuantity,
olderThan: logsOlderThan, olderThan: logsOlderThan,
responseStatus: selectedResultStatus, responseStatus: selectedResultStatus,
@ -246,8 +252,8 @@ class LogsProvider with ChangeNotifier {
clients: selectedClients clients: selectedClients
); );
if (result['result'] == 'success') { if (result.successful == true) {
LogsData newLogsData = result['data']; LogsData newLogsData = result.content as LogsData;
if (appliedFilters.clients != null) { if (appliedFilters.clients != null) {
newLogsData.data = newLogsData.data.where( newLogsData.data = newLogsData.data.where(
(item) => appliedFilters.clients!.contains(item.client) (item) => appliedFilters.clients!.contains(item.client)

View file

@ -35,14 +35,14 @@ class RewriteRulesProvider with ChangeNotifier {
} }
Future<bool> addDnsRewrite(RewriteRules rule) async { Future<bool> addDnsRewrite(RewriteRules rule) async {
final result = await _serversProvider!.apiClient!.addDnsRewriteRule( final result = await _serversProvider!.apiClient2!.addDnsRewriteRule(
data: { data: {
"domain": rule.domain, "domain": rule.domain,
"answer": rule.answer "answer": rule.answer
} }
); );
if (result['result'] == 'success') { if (result.successful == true) {
List<RewriteRules> data = rewriteRules!; List<RewriteRules> data = rewriteRules!;
data.add(rule); data.add(rule);
setRewriteRulesData(data); setRewriteRulesData(data);
@ -55,7 +55,7 @@ class RewriteRulesProvider with ChangeNotifier {
} }
Future<bool> editDnsRewrite(RewriteRules newRule, RewriteRules oldRule) async { Future<bool> editDnsRewrite(RewriteRules newRule, RewriteRules oldRule) async {
final result = await _serversProvider!.apiClient!.updateRewriteRule( final result = await _serversProvider!.apiClient2!.updateRewriteRule(
body: { body: {
"target": { "target": {
"answer": oldRule.answer, "answer": oldRule.answer,
@ -68,7 +68,7 @@ class RewriteRulesProvider with ChangeNotifier {
} }
); );
if (result['result'] == 'success') { if (result.successful == true) {
List<RewriteRules> data = rewriteRules!; List<RewriteRules> data = rewriteRules!;
final index = data.indexOf(oldRule); final index = data.indexOf(oldRule);
data[index] = newRule; data[index] = newRule;
@ -82,14 +82,14 @@ class RewriteRulesProvider with ChangeNotifier {
} }
Future<bool> deleteDnsRewrite(RewriteRules rule) async { Future<bool> deleteDnsRewrite(RewriteRules rule) async {
final result = await _serversProvider!.apiClient!.deleteDnsRewriteRule( final result = await _serversProvider!.apiClient2!.deleteDnsRewriteRule(
data: { data: {
"domain": rule.domain, "domain": rule.domain,
"answer": rule.answer "answer": rule.answer
} }
); );
if (result['result'] == 'success') { if (result.successful == true) {
List<RewriteRules> data = rewriteRules!; List<RewriteRules> data = rewriteRules!;
data = data.where((item) => item.domain != rule.domain).toList(); data = data.where((item) => item.domain != rule.domain).toList();
setRewriteRulesData(data); setRewriteRulesData(data);
@ -108,10 +108,10 @@ class RewriteRulesProvider with ChangeNotifier {
_loadStatus = LoadStatus.loading; _loadStatus = LoadStatus.loading;
} }
final result = await _serversProvider!.apiClient!.getDnsRewriteRules(); final result = await _serversProvider!.apiClient2!.getDnsRewriteRules();
if (result['result'] == 'success') { if (result.successful == true) {
_rewriteRules = result['data']; _rewriteRules = result.content as List<RewriteRules>;
_loadStatus = LoadStatus.loaded; _loadStatus = LoadStatus.loaded;
notifyListeners(); notifyListeners();
return true; return true;

View file

@ -3,9 +3,11 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:sqflite/sqflite.dart'; import 'package:sqflite/sqflite.dart';
import 'package:adguard_home_manager/models/github_release.dart';
import 'package:adguard_home_manager/services/api_client.dart';
import 'package:adguard_home_manager/services/external_requests.dart';
import 'package:adguard_home_manager/models/server.dart'; import 'package:adguard_home_manager/models/server.dart';
import 'package:adguard_home_manager/models/update_available.dart'; import 'package:adguard_home_manager/models/update_available.dart';
import 'package:adguard_home_manager/services/http_requests.dart';
import 'package:adguard_home_manager/functions/conversions.dart'; import 'package:adguard_home_manager/functions/conversions.dart';
import 'package:adguard_home_manager/services/db/queries.dart'; import 'package:adguard_home_manager/services/db/queries.dart';
import 'package:adguard_home_manager/constants/enums.dart'; import 'package:adguard_home_manager/constants/enums.dart';
@ -15,7 +17,8 @@ class ServersProvider with ChangeNotifier {
List<Server> _serversList = []; List<Server> _serversList = [];
Server? _selectedServer; Server? _selectedServer;
ApiClient? _apiClient; // ApiClient? _apiClient;
ApiClientV2? _apiClient2;
bool _updatingServer = false; bool _updatingServer = false;
@ -24,8 +27,12 @@ class ServersProvider with ChangeNotifier {
data: null, data: null,
); );
ApiClient? get apiClient { // ApiClient? get apiClient {
return _apiClient; // return _apiClient;
// }
ApiClientV2? get apiClient2 {
return _apiClient2;
} }
List<Server> get serversList { List<Server> get serversList {
@ -53,7 +60,7 @@ class ServersProvider with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void setSelectedServer(Server server) { void setSelectedServer(Server? server) {
_selectedServer = server; _selectedServer = server;
notifyListeners(); notifyListeners();
} }
@ -70,8 +77,13 @@ class ServersProvider with ChangeNotifier {
notifyListeners(); notifyListeners();
} }
void setApiClient(ApiClient client) { // void setApiClient(ApiClient client) {
_apiClient = client; // _apiClient = client;
// notifyListeners();
// }
void setApiClient2(ApiClientV2 client) {
_apiClient2 = client;
notifyListeners(); notifyListeners();
} }
@ -141,7 +153,8 @@ class ServersProvider with ChangeNotifier {
_serversList = newServers; _serversList = newServers;
if (selectedServer != null &&server.id == selectedServer!.id) { if (selectedServer != null &&server.id == selectedServer!.id) {
_apiClient = ApiClient(server: server); // _apiClient = ApiClient(server: server);
_apiClient2 = ApiClientV2(server: server);
} }
notifyListeners(); notifyListeners();
@ -156,7 +169,7 @@ class ServersProvider with ChangeNotifier {
final result = await removeServerQuery(_dbInstance!, server.id); final result = await removeServerQuery(_dbInstance!, server.id);
if (result == true) { if (result == true) {
_selectedServer = null; _selectedServer = null;
_apiClient = null; // _apiClient = null;
List<Server> newServers = _serversList.where((s) => s.id != server.id).toList(); List<Server> newServers = _serversList.where((s) => s.id != server.id).toList();
_serversList = newServers; _serversList = newServers;
notifyListeners(); notifyListeners();
@ -169,14 +182,16 @@ class ServersProvider with ChangeNotifier {
void checkServerUpdatesAvailable({ void checkServerUpdatesAvailable({
required Server server, required Server server,
ApiClientV2? apiClient
}) async { }) async {
final client = apiClient ?? _apiClient2;
setUpdateAvailableLoadStatus(LoadStatus.loading, true); setUpdateAvailableLoadStatus(LoadStatus.loading, true);
final result = await _apiClient!.checkServerUpdates(); final result = await client!.checkServerUpdates();
if (result['result'] == 'success') { if (result.successful == true) {
UpdateAvailableData data = UpdateAvailableData.fromJson(result['data']); UpdateAvailableData data = UpdateAvailableData.fromJson(result.content);
final gitHubResult = await _apiClient!.getUpdateChangelog(releaseTag: data.newVersion ?? data.currentVersion); final gitHubResult = await ExternalRequests.getReleaseData(releaseTag: data.newVersion ?? data.currentVersion);
if (gitHubResult['result'] == 'success') { if (gitHubResult.successful == true) {
data.changelog = gitHubResult['body']; data.changelog = (gitHubResult.content as GitHubRelease).body;
} }
setUpdateAvailableData(data); setUpdateAvailableData(data);
setUpdateAvailableLoadStatus(LoadStatus.loaded, true); setUpdateAvailableLoadStatus(LoadStatus.loaded, true);
@ -186,11 +201,12 @@ class ServersProvider with ChangeNotifier {
} }
} }
Future initializateServer(Server server) async { Future initializateServer(Server server, /*ApiClient apiClient, */ ApiClientV2 apiClient2) async {
final serverStatus = await _apiClient!.getServerStatus(); final serverStatus = await _apiClient2!.getServerStatus();
if (serverStatus['result'] == 'success') { if (serverStatus.successful == true) {
checkServerUpdatesAvailable( // Do not await checkServerUpdatesAvailable( // Do not await
server: server, server: server,
apiClient: apiClient2
); );
} }
} }
@ -222,8 +238,11 @@ class ServersProvider with ChangeNotifier {
if (defaultServer != null) { if (defaultServer != null) {
_selectedServer = defaultServer; _selectedServer = defaultServer;
_apiClient = ApiClient(server: defaultServer); // final client = ApiClient(server: defaultServer);
initializateServer(defaultServer); final client2 = ApiClientV2(server: defaultServer);
// _apiClient = client;
_apiClient2 = client2;
initializateServer(defaultServer, /*client,*/ client2);
} }
} }
else { else {
@ -240,13 +259,13 @@ class ServersProvider with ChangeNotifier {
const Duration(seconds: 2), const Duration(seconds: 2),
(timer) async { (timer) async {
if (_selectedServer != null && _selectedServer == server) { if (_selectedServer != null && _selectedServer == server) {
final result = await _apiClient!.checkServerUpdates(); final result = await _apiClient2!.checkServerUpdates();
if (result['result'] == 'success') { if (result.successful == true) {
UpdateAvailableData data = UpdateAvailableData.fromJsonUpdate(result['data']); UpdateAvailableData data = UpdateAvailableData.fromJsonUpdate(result.content);
if (data.currentVersion == data.newVersion) { if (data.currentVersion == data.newVersion) {
final gitHubResult = await _apiClient!.getUpdateChangelog(releaseTag: data.newVersion ?? data.currentVersion); final gitHubResult = await ExternalRequests.getReleaseData(releaseTag: data.newVersion ?? data.currentVersion);
if (gitHubResult['result'] == 'success') { if (gitHubResult.successful == true) {
data.changelog = gitHubResult['body']; data.changelog = (gitHubResult.content as GitHubRelease).body;
} }
setUpdateAvailableData(data); setUpdateAvailableData(data);
timer.cancel(); timer.cancel();

View file

@ -2,11 +2,15 @@ import 'dart:async';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:adguard_home_manager/widgets/add_server/unsupported_version_modal.dart';
import 'package:adguard_home_manager/config/globals.dart';
import 'package:adguard_home_manager/config/minimum_server_version.dart';
import 'package:adguard_home_manager/functions/compare_versions.dart';
import 'package:adguard_home_manager/models/server_status.dart'; import 'package:adguard_home_manager/models/server_status.dart';
import 'package:adguard_home_manager/models/filtering_status.dart'; import 'package:adguard_home_manager/models/filtering_status.dart';
import 'package:adguard_home_manager/constants/enums.dart'; import 'package:adguard_home_manager/constants/enums.dart';
import 'package:adguard_home_manager/providers/servers_provider.dart'; import 'package:adguard_home_manager/providers/servers_provider.dart';
import 'package:adguard_home_manager/functions/compare_versions.dart';
import 'package:adguard_home_manager/functions/time_server_disabled.dart'; import 'package:adguard_home_manager/functions/time_server_disabled.dart';
class StatusProvider with ChangeNotifier { class StatusProvider with ChangeNotifier {
@ -104,7 +108,7 @@ class StatusProvider with ChangeNotifier {
} }
} }
Future<dynamic> updateBlocking({ Future<bool> updateBlocking({
required String block, required String block,
required bool newStatus, required bool newStatus,
int? time int? time
@ -114,14 +118,14 @@ class StatusProvider with ChangeNotifier {
_protectionsManagementProcess.add('general'); _protectionsManagementProcess.add('general');
notifyListeners(); notifyListeners();
final result = await _serversProvider!.apiClient!.updateGeneralProtection( final result = await _serversProvider!.apiClient2!.updateGeneralProtection(
enable: newStatus, enable: newStatus,
time: time time: time
); );
_protectionsManagementProcess = _protectionsManagementProcess.where((e) => e != 'general').toList(); _protectionsManagementProcess = _protectionsManagementProcess.where((e) => e != 'general').toList();
if (result['result'] == 'success') { if (result.successful == true) {
_serverStatus!.generalEnabled = newStatus; _serverStatus!.generalEnabled = newStatus;
if (time != null) { if (time != null) {
final deadline = generateTimeDeadline(time); final deadline = generateTimeDeadline(time);
@ -135,111 +139,80 @@ class StatusProvider with ChangeNotifier {
stopCountdown(); stopCountdown();
} }
notifyListeners(); notifyListeners();
return null; return true;
} }
else { else {
notifyListeners(); return false;
return result['log'];
} }
case 'general_legacy':
_protectionsManagementProcess.add('general');
notifyListeners();
final result = await _serversProvider!.apiClient!.updateGeneralProtectionLegacy(newStatus);
_protectionsManagementProcess = _protectionsManagementProcess.where((e) => e != 'general').toList();
if (result['result'] == 'success') {
_serverStatus!.generalEnabled = newStatus;
notifyListeners();
return null;
}
else {
notifyListeners();
return result['log'];
}
case 'filtering': case 'filtering':
_protectionsManagementProcess.add('filtering'); _protectionsManagementProcess.add('filtering');
notifyListeners(); notifyListeners();
final result = await _serversProvider!.apiClient!.updateFiltering( final result = await _serversProvider!.apiClient2!.updateFiltering(
enable: newStatus, enable: newStatus,
); );
_protectionsManagementProcess = _protectionsManagementProcess.where((e) => e != 'filtering').toList(); _protectionsManagementProcess = _protectionsManagementProcess.where((e) => e != 'filtering').toList();
if (result['result'] == 'success') { if (result.successful == true) {
_serverStatus!.filteringEnabled = newStatus; _serverStatus!.filteringEnabled = newStatus;
notifyListeners(); notifyListeners();
return null; return true;
} }
else { else {
return false;
notifyListeners();
return result['log'];
} }
case 'safeSearch': case 'safeSearch':
_protectionsManagementProcess.add('safeSearch'); _protectionsManagementProcess.add('safeSearch');
notifyListeners(); notifyListeners();
final result = serverVersionIsAhead( final result = await _serversProvider!.apiClient2!.updateSafeSearchSettings(body: { 'enabled': newStatus });
currentVersion: serverStatus!.serverVersion,
referenceVersion: 'v0.107.28',
referenceVersionBeta: 'v0.108.0-b.33'
) == true
? await _serversProvider!.apiClient!.updateSafeSearchSettings(body: { 'enabled': newStatus })
: await _serversProvider!.apiClient!.updateSafeSearchLegacy(newStatus);
_protectionsManagementProcess = _protectionsManagementProcess.where((e) => e != 'safeSearch').toList(); _protectionsManagementProcess = _protectionsManagementProcess.where((e) => e != 'safeSearch').toList();
if (result['result'] == 'success') { if (result.successful == true) {
_serverStatus!.safeSearchEnabled = newStatus; _serverStatus!.safeSearchEnabled = newStatus;
notifyListeners(); notifyListeners();
return null; return true;
} }
else { else {
notifyListeners(); return false;
return result['log'];
} }
case 'safeBrowsing': case 'safeBrowsing':
_protectionsManagementProcess.add('safeBrowsing'); _protectionsManagementProcess.add('safeBrowsing');
notifyListeners(); notifyListeners();
final result = await _serversProvider!.apiClient!.updateSafeBrowsing(newStatus); final result = await _serversProvider!.apiClient2!.updateSafeBrowsing(enable: newStatus);
_protectionsManagementProcess = _protectionsManagementProcess.where((e) => e != 'safeBrowsing').toList(); _protectionsManagementProcess = _protectionsManagementProcess.where((e) => e != 'safeBrowsing').toList();
if (result['result'] == 'success') { if (result.successful == true) {
_serverStatus!.safeBrowsingEnabled = newStatus; _serverStatus!.safeBrowsingEnabled = newStatus;
notifyListeners(); notifyListeners();
return null; return true;
} }
else { else {
notifyListeners(); return false;
return result['log'];
} }
case 'parentalControl': case 'parentalControl':
_protectionsManagementProcess.add('parentalControl'); _protectionsManagementProcess.add('parentalControl');
notifyListeners(); notifyListeners();
final result = await _serversProvider!.apiClient!.updateParentalControl(newStatus); final result = await _serversProvider!.apiClient2!.updateParentalControl(enable: newStatus);
_protectionsManagementProcess = _protectionsManagementProcess.where((e) => e != 'parentalControl').toList(); _protectionsManagementProcess = _protectionsManagementProcess.where((e) => e != 'parentalControl').toList();
if (result['result'] == 'success') { if (result.successful == true) {
_serverStatus!.parentalControlEnabled = newStatus; _serverStatus!.parentalControlEnabled = newStatus;
notifyListeners(); notifyListeners();
return null; return true;
} }
else { else {
notifyListeners(); return false;
return result['log'];
} }
default: default:
@ -252,9 +225,9 @@ class StatusProvider with ChangeNotifier {
} }
Future<bool> getFilteringRules() async { Future<bool> getFilteringRules() async {
final result = await _serversProvider!.apiClient!.getFilteringRules(); final result = await _serversProvider!.apiClient2!.getFilteringRules();
if (result['result'] == 'success') { if (result.successful == true) {
_filteringStatus = result['data']; _filteringStatus = result.content as FilteringStatus;
notifyListeners(); notifyListeners();
return true; return true;
} }
@ -264,19 +237,39 @@ class StatusProvider with ChangeNotifier {
} }
Future<bool> getServerStatus({ Future<bool> getServerStatus({
bool? withLoadingIndicator bool? withLoadingIndicator = true,
bool? overrideCheckServerVersion
}) async { }) async {
if (withLoadingIndicator == true) { if (withLoadingIndicator == true) {
_loadStatus = LoadStatus.loading; _loadStatus = LoadStatus.loading;
} }
final result = await _serversProvider!.apiClient!.getServerStatus(); final result = await _serversProvider!.apiClient2!.getServerStatus();
if (result['result'] == 'success') { if (result.successful == true) {
final status = result.content as ServerStatus;
setServerStatusData( setServerStatusData(
data: result['data'] data: status
); );
_loadStatus = LoadStatus.loaded; _loadStatus = LoadStatus.loaded;
notifyListeners(); notifyListeners();
// Check server version and launch modal if not valid
final validVersion = serverVersionIsAhead(
currentVersion: status.serverVersion,
referenceVersion: MinimumServerVersion.stable,
referenceVersionBeta: MinimumServerVersion.beta
);
if (validVersion == false && overrideCheckServerVersion != true) {
showDialog(
context: globalNavigatorKey.currentContext!,
builder: (ctx) => UnsupportedVersionModal(
serverVersion: status.serverVersion,
onClose: () {
_serversProvider!.setSelectedServer(null);
}
)
);
}
return true; return true;
} }
else { else {
@ -292,12 +285,12 @@ class StatusProvider with ChangeNotifier {
}) async { }) async {
if (_serverStatus == null) return false; if (_serverStatus == null) return false;
final rules = await _serversProvider!.apiClient!.getFilteringRules(); final rules = await _serversProvider!.apiClient2!.getFilteringRules();
if (rules['result'] == 'success') { if (rules.successful == true) {
FilteringStatus oldStatus = _serverStatus!.filteringStatus; FilteringStatus oldStatus = _serverStatus!.filteringStatus;
List<String> newRules = rules['data'].userRules.where((d) => !d.contains(domain)).toList(); List<String> newRules = (rules.content as FilteringStatus).userRules.where((d) => !d.contains(domain)).toList();
if (newStatus == 'block') { if (newStatus == 'block') {
newRules.add("||$domain^"); newRules.add("||$domain^");
} }
@ -308,9 +301,9 @@ class StatusProvider with ChangeNotifier {
newObj.userRules = newRules; newObj.userRules = newRules;
_filteringStatus = newObj; _filteringStatus = newObj;
final result = await _serversProvider!.apiClient!.postFilteringRules(data: {'rules': newRules}); final result = await _serversProvider!.apiClient2!.postFilteringRules(data: {'rules': newRules});
if (result['result'] == 'success') { if (result.successful == true) {
return true; return true;
} }
else { else {
@ -324,11 +317,11 @@ class StatusProvider with ChangeNotifier {
} }
Future<bool> updateSafeSearchConfig(Map<String, bool> status) async { Future<bool> updateSafeSearchConfig(Map<String, bool> status) async {
final result = await _serversProvider!.apiClient!.updateSafeSearchSettings( final result = await _serversProvider!.apiClient2!.updateSafeSearchSettings(
body: status body: status
); );
if (result['result'] == 'success') { if (result.successful == true) {
ServerStatus data = serverStatus!; ServerStatus data = serverStatus!;
data.safeSearchEnabled = status['enabled'] ?? false; data.safeSearchEnabled = status['enabled'] ?? false;
data.safeSeachBing = status['bing'] ?? false; data.safeSeachBing = status['bing'] ?? false;

View file

@ -1,7 +1,5 @@
// ignore_for_file: use_build_context_synchronously // ignore_for_file: use_build_context_synchronously
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:animations/animations.dart'; import 'package:animations/animations.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
@ -9,11 +7,10 @@ import 'package:flutter_split_view/flutter_split_view.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:adguard_home_manager/screens/clients/client_screen.dart'; import 'package:adguard_home_manager/screens/clients/client/client_screen_functions.dart';
import 'package:adguard_home_manager/screens/clients/added_client_tile.dart'; import 'package:adguard_home_manager/screens/clients/client/added_client_tile.dart';
import 'package:adguard_home_manager/screens/clients/remove_client_modal.dart'; import 'package:adguard_home_manager/screens/clients/client/remove_client_modal.dart';
import 'package:adguard_home_manager/screens/clients/fab.dart'; import 'package:adguard_home_manager/screens/clients/fab.dart';
import 'package:adguard_home_manager/screens/clients/options_modal.dart';
import 'package:adguard_home_manager/widgets/tab_content_list.dart'; import 'package:adguard_home_manager/widgets/tab_content_list.dart';
import 'package:adguard_home_manager/functions/snackbar.dart'; import 'package:adguard_home_manager/functions/snackbar.dart';
@ -32,13 +29,13 @@ class AddedList extends StatefulWidget {
final bool splitView; final bool splitView;
const AddedList({ const AddedList({
Key? key, super.key,
required this.scrollController, required this.scrollController,
required this.data, required this.data,
required this.onClientSelected, required this.onClientSelected,
this.selectedClient, this.selectedClient,
required this.splitView required this.splitView
}) : super(key: key); });
@override @override
State<AddedList> createState() => _AddedListState(); State<AddedList> createState() => _AddedListState();
@ -77,8 +74,8 @@ class _AddedListState extends State<AddedList> {
final width = MediaQuery.of(context).size.width; final width = MediaQuery.of(context).size.width;
void confirmEditClient(Client client) async { void confirmEditClient(Client client) async {
ProcessModal processModal = ProcessModal(context: context); ProcessModal processModal = ProcessModal();
processModal.open(AppLocalizations.of(context)!.addingClient); processModal.open(AppLocalizations.of(context)!.savingChanges);
final result = await clientsProvider.editClient(client); final result = await clientsProvider.editClient(client);
@ -101,7 +98,7 @@ class _AddedListState extends State<AddedList> {
} }
void deleteClient(Client client) async { void deleteClient(Client client) async {
ProcessModal processModal = ProcessModal(context: context); ProcessModal processModal = ProcessModal();
processModal.open(AppLocalizations.of(context)!.removingClient); processModal.open(AppLocalizations.of(context)!.removingClient);
final result = await clientsProvider.deleteClient(client); final result = await clientsProvider.deleteClient(client);
@ -128,31 +125,13 @@ class _AddedListState extends State<AddedList> {
} }
void openClientModal(Client client) { void openClientModal(Client client) {
if (width > 900 || !(Platform.isAndroid | Platform.isIOS)) { openClientFormModal(
showDialog( context: context,
barrierDismissible: false, width: width,
context: context, client: client,
builder: (BuildContext context) => ClientScreen( onConfirm: confirmEditClient,
onConfirm: confirmEditClient, onDelete: deleteClient
serverVersion: statusProvider.serverStatus!.serverVersion, );
onDelete: deleteClient,
client: client,
dialog: true,
)
);
}
else {
Navigator.push(context, MaterialPageRoute(
fullscreenDialog: true,
builder: (BuildContext context) => ClientScreen(
onConfirm: confirmEditClient,
serverVersion: statusProvider.serverStatus!.serverVersion,
onDelete: deleteClient,
client: client,
dialog: false,
)
));
}
} }
void openDeleteModal(Client client) { void openDeleteModal(Client client) {
@ -163,19 +142,13 @@ class _AddedListState extends State<AddedList> {
) )
); );
} }
final clientsDisplay = clientsProvider.searchTermClients != null && clientsProvider.searchTermClients != ""
void openOptionsModal(Client client) { ? widget.data.where(
showModal( (c) => c.name.toLowerCase().contains(clientsProvider.searchTermClients.toString()) || c.ids.where((id) => id.contains(clientsProvider.searchTermClients.toString())).isNotEmpty
context: context, ).toList()
builder: (ctx) => OptionsModal( : widget.data;
onDelete: () => openDeleteModal(client),
onEdit: () => openClientModal(client),
)
);
}
return CustomTabContentList( return CustomTabContentList(
noSliver: !(Platform.isAndroid || Platform.isIOS),
listPadding: widget.splitView == true listPadding: widget.splitView == true
? const EdgeInsets.only(top: 8) ? const EdgeInsets.only(top: 8)
: null, : null,
@ -198,15 +171,16 @@ class _AddedListState extends State<AddedList> {
], ],
), ),
), ),
itemsCount: widget.data.length, itemsCount: clientsDisplay.length,
contentWidget: (index) => AddedClientTile( contentWidget: (index) => AddedClientTile(
selectedClient: widget.selectedClient, selectedClient: widget.selectedClient,
client: widget.data[index], client: clientsDisplay[index],
onTap: widget.onClientSelected, onTap: widget.onClientSelected,
onLongPress: openOptionsModal, onEdit: statusProvider.serverStatus != null
onEdit: openClientModal, ? (c) => openClientModal(c)
: null,
onDelete: openDeleteModal,
splitView: widget.splitView, splitView: widget.splitView,
serverVersion: statusProvider.serverStatus!.serverVersion,
), ),
noData: SizedBox( noData: SizedBox(
width: double.maxFinite, width: double.maxFinite,

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